次の不自然なエンティティ オブジェクトを考えてみましょう。
public class Consumer
{
public int Id { get; set; }
public string Name { get; set; }
public bool NeedsProcessed { get; set; }
public virtual IList<Purchase> Purchases { get; set; } //virtual so EF can lazy-load
}
public class Purchase
{
public int Id { get; set; }
public decimal TotalCost { get; set; }
public int ConsumerId { get; set; }
}
ここで、このコードを実行したいとしましょう:
var consumers = Consumers.Where(consumer => consumer.NeedsProcessed);
//assume that ProcessConsumers accesses the Consumer.Purchases property
SomeExternalServiceICannotModify.ProcessConsumers(consumers);
デフォルトでは、これは ProcessConsumers メソッド内の Select N+1 の影響を受けます。消費者を列挙するときにクエリをトリガーし、各購入コレクションを 1 つずつ取得します。この問題の標準的な解決策は、インクルードを追加することです。
var consumers = Consumers.Include("Purchases").Where(consumer => consumer.NeedsProcessed);
//assume that ProcessConsumers accesses the Consumer.Purchases property
SomeExternalServiceICannotModify.ProcessConsumers(consumers);
多くの場合、これで問題なく動作しますが、複雑なケースでは、インクルードによってパフォーマンスが桁違いに低下する可能性があります。次のようなことは可能ですか?
- 消費者をつかむ、var consumer = _entityContext.Consumers.Where(...).ToList()
- 私の購入をつかむ、var purchases = _entityContext.Purchases.Where(...).ToList()
- 消費者に潤いを与えます。すでにメモリにロードされている購入からコレクションを手動で購入します。次に、それを ProcessConsumers に渡すと、それ以上の db クエリはトリガーされません。
#3 やり方がわかりません。遅延ロード (したがって Select N+1) をトリガーする consumer.Purchases コレクションにアクセスしようとするとします。おそらく、Consumer を適切な型 (EF プロキシ型ではなく) にキャストしてから、コレクションをロードする必要がありますか? このようなもの:
foreach (var consumer in Consumers)
{
//since the EF proxy overrides the Purchases property, this doesn't really work, I'm trying to figure out what would
((Consumer)consumer).Purchases = purchases.Where(x => x.ConsumerId = consumer.ConsumerId).ToList();
}
編集: 問題をより明確に明らかにするために、例を少し書き直しました。