我听说EF4与NHibernate相关的少数有效投诉之一是EF4在处理延迟加载的集合方面很差。例如,在一个延迟加载的集合中,如果我说:
if (MyAccount.Orders.Count() > 0) ;
EF会将整个系列拉下来(如果还没有),而NH会足够聪明地发出select count(*)
NH还有一些不错的批量提取来帮助解决select n + 1
问题。据我了解,最接近的EF4可以使用Include方法。
EF团队是否允许任何迹象表明这将在下一次迭代中修复?我知道他们在POCO上很努力,但这似乎是一个受欢迎的解决方案。
答案 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
。