在下一个实体框架中选择N + 1

时间:2011-02-21 18:57:30

标签: entity-framework

我听说EF4与NHibernate相关的少数有效投诉之一是EF4在处理延迟加载的集合方面很差。例如,在一个延迟加载的集合中,如果我说:

if (MyAccount.Orders.Count() > 0) ;

EF会将整个系列拉下来(如果还没有),而NH会足够聪明地发出select count(*)

NH还有一些不错的批量提取来帮助解决select n + 1问题。据我了解,最接近的EF4可以使用Include方法。

EF团队是否允许任何迹象表明这将在下一次迭代中修复?我知道他们在POCO上很努力,但这似乎是一个受欢迎的解决方案。

1 个答案:

答案 0 :(得分:13)

您描述的不是N + 1问题。 N + 1问题的例子是here。 N + 1表示您执行N + 1次选择而不是一次(或两次)。在你的例子中,它很可能意味着:

// Lazy loads all N Orders in single select
foreach(var order in MyAccount.Orders)
{
  // Lazy loads all Items for single order => executed N times
  foreach(var orderItem in order.Items)
  {
     ...
  }
}

这可以通过以下方式轻松解决:

// Eager load all Orders and their items in single query
foreach(var order in context.Accounts.Include("Orders.Items").Where(...))
{
 ...
}

您的示例对我有效。您有一个公开IEnumerable的集合,并对其执行Count操作。集合是延迟加载的,计数在内存中执行。将Linq查询转换为SQL的功能仅在IQueryable上可用,表达式树表示查询。但IQueryable表示查询=每次访问意味着在DB中执行新操作,例如,检查循环中的Count将在每次迭代中执行数据库查询。

所以更多的是关于动态代理的实现。


在使用DbContext代替ObjectContext而不是通过与集合的直接交互时,代码优先CTP5(最终版本将被称为EF 4.1)已经可以计算相关实体而不加载它们。你将不得不使用类似的东西:

int count = context.Entry(myAccount).Collection(a => a.Orders).Query().Count();

Query方法返回准备好的IQueryable,如果您使用延迟加载,可能会运行EF,但您可以进一步修改查询 - 这里我使用了Count