每次访问导航属性时都会读取它们吗? (EF4.1)

时间:2011-06-10 16:13:16

标签: entity-framework poco entity-framework-4.1 ef-code-first

我正在使用EF 4.1,其中POCO是延迟加载的。

我运行的一些示例查询:

var discountsCount = product.Discounts.Count();  
var currentDiscountsCount = product.Discounts.Count(d=>d.IsCurrent);  
var expiredDiscountsCount = product.Discounts.Count(d=>d.IsExpired);  

我想知道的是,我的疑问是否有意义,或者表现不佳:

我每次都会访问数据库,还是结果来自DbContext中的缓存数据?

每次都可以“从头开始”访问导航属性,如上所述,或者我应该缓存它们然后对它们执行更多查询,例如:

var discounts = product.Discounts;  
var current = discounts.Count(d=>d.IsCurrent);  
var expired = discounts.Count(d=>d.Expired);  

如下所示的复杂情况如何,它会拉动整个集合然后对其执行本地操作,还是构建一个专门的SQL查询,这意味着我无法重用结果以避免再次访问数据库:

var chained = discounts.OrderBy(d=>d.CreationDate).Where(d=>d.CreationDate < DateTime.Now).Count();

感谢您的建议!

根据以下评论进行编辑

因此,一旦我调用导航属性(它是一个集合),它将加载整个对象图。但是,如果我使用.Count(d=>d...)Select(d=>d...)Min(d=>d...)等过滤了该集合,它会加载整个图表,还是只加载最终数据呢?

2 个答案:

答案 0 :(得分:4)

product.Discounts(或任何其他导航集)不是IQueryable,而只是IEnumerable。您在product.Discounts上执行的LINQ操作永远不会向数据库发出查询 - 唯一的例外是,在延迟加载product.Discounts的情况下,将从数据库加载一次到内存中。它将被完全加载 - 无论您执行哪种LINQ操作或过滤器。

如果要在不将集合完全加载到内存中的情况下对导航集合执行过滤器或任何查询,则不得访问导航集合,而是通过上下文创建查询,例如在您的示例中:

var chained = context.Entry(product).Collection(p => p.Discounts).Query()
                     .Where(d => d.CreationDate < DateTime.Now).Count();

这不会将Discounts product集合加载到内存中,但会在数据库中执行查询,然后返回单个数字作为结果。这种下一个查询将再次进入数据库。

答案 1 :(得分:3)

在上面的示例中,首次访问时,应使用Ef填充折扣集合。然后应在内存中执行对Discount集合的后续linq查询。这甚至包括最后一个复杂的表达式。

您还可以使用Include方法确保首次返回关联集合。例如.Include(“折扣”);

如果您担心性能,我建议您使用SQL事件探查器来查看正在执行的SQL。