考虑这些人为的实体对象:
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 的影响。它会在枚举消费者时触发一个查询,然后它将一个一个地抓取每个购买集合。这个问题的标准解决方案是添加一个包含:
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 purchase = _entityContext.Purchases.Where(...).ToList()
- 水合消费者。从我已经加载到内存中的购买中手动购买集合。然后,当我将它传递给 ProcessConsumers 时,它不会触发更多的数据库查询。
我不确定如何做#3。如果您尝试访问任何会触发延迟加载(以及因此 Select N+1)的 consumer.Purchases 集合。也许我需要将消费者转换为正确的类型(而不是 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();
}
编辑: 我已经重写了这个例子,希望能更清楚地揭示这个问题。