2016-08-25 10 views
1
は、私は、次のLINQクエリを持って

可能な遅延ロードを保つ:二つの異なるコンテキストでクエリを実行するが、

//two different contexts, databases, tables... 
NoteSet = lmCtx.LMNotes.AsEnumerable(); 
EmpSet = tessCtx.Employees.AsEnumerable(); 

var lmAccountNotes = (from lmnote in NoteSet 
         join createdby in EmpSet on lmnote.lnt_createdById equals createdby.EmployeeID 
         join modifiedby in EmpSet on lmnote.lnt_modifiedById equals modifiedby.EmployeeID 
         where lmnote.lnt_recordId == 5566 && lmnote.lnt_tableId == 1 
         select new NoteInfo { Note = lmnote, CreatedBy = createdby, ModifiedBy = modifiedby }).ToList(); 

これは、小さなテーブルに対するクエリのために動作しますが、NoteSetはかなり大きな表であると私はうまく到達していますフレームワークがちょうど爆発して、OutOfMemory例外をスローする前に、プロセスによって1.5GB以上の使用済みメモリが残っています。

このような実行中に遅延読み込み機能を使用する方法はありますか? NoteInfoオブジェクトを返すクエリを持つ維持するよう

+1

一見すると、すべてのものを '.AsEnumerable() 'で実体化し、それを照会します。これはメモリの問題の原因かもしれません。それを削除し、それを標準の 'IQueryable'インタフェースとして使用することで、メモリ使用量が目立って減少するはずです。 – Scott

+0

はい、次のエラーが表示されます: '指定されたLINQ式には、異なるコンテキストに関連付けられたクエリへの参照が含まれています。これが私が 'AsEnumerable()'を使用した理由です –

+0

'AsEnumerable()'を使うことで、あなたは本質的にあなたの全体の "クエリ"をメモリ上で実行しています。したがって、データベースからすべてのデータをフェッチしてから、アプリケーション内で結合します。データベースでクエリを効率的に実行するには、 'IQueryable'を使用する必要があります。したがって、両方のエンティティにアクセスできる単一のデータベースコンテキストが必要です。その周りには道はない。 – poke

答えて

0

だから、私はこれにそれを変更:コメントで説明したように

//LMNotes is the actual huge database... 
var m = lmCtx.LMNotes.Where(x => x.lnt_recordId == 5566).ToList(); 

var lmAccountNotes = (from lmnote in m 
      join createdby in EmpSet on lmnote.lnt_createdById equals createdby.EmployeeID 
      join modifiedby in EmpSet on lmnote.lnt_modifiedById equals modifiedby.EmployeeID 
      where lmnote.lnt_recordId == 566 && lmnote.lnt_tableId == 1 
      select new NoteInfo { Note = lmnote, CreatedBy = createdby, ModifiedBy = modifiedby }).ToList(); 

これは

0

優れている、あなたは本当に、単一のクエリを実行することはできません少なくとも2つの異なるデータベースにまたがっていて、少なくともいくつかのヘルプ構成を設定する必要はありません(いずれのデータベースにも存在し、実際にパフォーマンスを向上させるかどうかは分かりません)。

しかし、それはあなたのクエリを改善することができないということを意味するものではありません。クエリを実行するデータベースエンジンに頼ることができない場合、私たちはそれを自分で行うことができます。この場合、基本的にはLMNotesエンティティのクエリだけです。次に、Employeesセットの従業員に参加します。

だから、素朴な解決策は、次のようになります。もちろん

var notes = lmCtx.LMNotes 
    .Where(lmnote => lmnote.lnt_recordId == 5566 && lmnote.lnt_tableId == 1) 
    .Select(lmnote => 
    { 
     return new NoteInfo 
     { 
      Note = lmnote, 
      CreatedBy = tessCtx.Employees.FirstOrDefault(e => e.EmployeeId == lmnote.lnt_createdById), 
      ModifiedBy = tessCtx.Employees.FirstOrDefault(e => e.EmployeeId == lmnote.lnt_modifiedById) 
     }; 
    }) 
    .ToList(); 

を、これはLMNotes上の単一のクエリを実行しながら、これはまだ結果における各ノートのための2つの別々のクエリを実行します。それで、EFがそこでやったことよりも実際には良いことではありません。

しかし、私たちができることは、ルックアップを追加することです。私は、従業員のセットは多少限定されていると思うので、一度各従業員を取得するだけで意味があります。

private Dictionary<int, Employee> employees = new Dictionary<int, Employee>(); 

private Employee GetEmployee(int employeeId) 
{ 
    Employee employee; 
    if (!employees.TryGetValue(employeeId, out employee)) 
    { 
     employee = tessCtx.Employees.FirstOrDefault(e => e.EmployeeId == employeeId); 
     employees[employeeId] = employee; 
    } 
    return employee; 
} 

public List<NoteInfo> GetNotes() 
{ 
    return lmCtx.LMNotes 
     .Where(lmnote => lmnote.lnt_recordId == 5566 && lmnote.lnt_tableId == 1) 
     .Select(lmnote => 
     { 
      return new NoteInfo 
      { 
       Note = lmnote, 
       CreatedBy = GetEmployee(lmnote.lnt_createdById), 
       ModifiedBy = GetEmployee(lmnote.lnt_modifiedById) 
      }; 
     }) 
     .ToList(); 
} 

これは、各従業員を一度検索して従業員オブジェクトをキャッシュするだけです。

また、ここで2回目の通過を行い、最初にメモを読み込んだ後にすべての従業員を一度に取得することもできます。このようなもの:

public List<NoteInfo> GetNotes() 
{ 
    var employeeIds = new HashSet<int>(); 

    var notes = lmCtx.LMNotes 
     .Where(lmnote => lmnote.lnt_recordId == 5566 && lmnote.lnt_tableId == 1) 
     .Select(lmnote => 
     { 
      // remember the ids for later 
      employeeIds.Add(lmnote.lnt_createdById); 
      employeeIds.Add(lmnote.lnt_modifiedById); 

      return new NoteInfo 
      { 
       Note = lmnote, 
       CreatedBy = null, 
       ModifiedBy = null 
      }; 
     }) 
     .ToList(); 

    var employees = tessCtx.Employees 
     .Where(e => employeeIds.Contains(e.EmployeeId)) 
     .ToList() 
     .ToDictionary(e => e.EmployeeId); 

    foreach (var noteInfo in notes) 
    { 
     noteInfo.CreatedBy = employees[noteInfo.Note.lnt_createdById]; 
     noteInfo.ModifiedBy = employees[noteInfo.Note.lnt_modifiedById]; 
    } 

    return notes; 
} 

これは、各データベースに対して1つのクエリしか実行しません。

関連する問題