Entity Framework是否在应用Where过滤器时选择内存中的所有行?

时间:2015-01-08 05:29:09

标签: c# sql-server entity-framework

有一件事让我感到困惑。我认为EF选择表中的所有行(所有记录)。

让我给你看一个例子。

public Category GetByID(int Id)
{
    return context.Categories.Find(Id);
}

表中有很多记录,当我用断点检查它们时,我可以看到所有记录不仅是Id编号的记录。如果表中有10k记录怎么办?我测试了这个。我在数据库中手动复制了所有记录,并制作了30k记录。

这样的表达式,

IEnumerable<Category> categories = categoryRepository
      .Where(x => x.Published == true)
      .ToList();

我看到了30k的断点记录。但至少有10k在表中发布了错误。

实体框架是否首先将所有记录提取到内存并过滤后?

1 个答案:

答案 0 :(得分:3)

<强> TL; DR

您的categoryRepository可能已损坏EF的IQueryable<>表达式树,并且正在实现整个Category表格以运行.Where谓词。请参阅以下示例。

更多详情

简短的回答是否定,只要实体框架能够解析IQueryable<>表达式(包括您指定的.Where谓词),它将使用适当的关联表达式树转换为本机Sql您正在寻找的RDBMS的查询提供程序,从而允许Sql的所有好处,例如使用索引。

根据我的评论,EF不会这样做的一个常见原因是,IQueryable机制是否被篡改,例如,如果您的Repository模式实现使用IEnumerable<T> {{ 3}}而不是overload of the Where predicate

因此,EF没有其他选择,只能获取表并针对谓词函数执行每一行,以确定该行是否与谓词匹配。

顺便说一下,有一些IQueryable overload是否有将DbContext包装在Repository和/或Unit of Work包装器中的优点,因为DbContext是Transactional,执行缓存,可以嘲笑在单元测试期间,现在支持各种数据库。

实现物化的示例及其对性能的影响

(实际执行Sql查询的点通常称为materialization

我排除了OP的存储库 - 即我们直接使用DbContext

最佳:

var miniFoos = myDbContext.MyFooSet
  .Where(f => f.SomeProperty = "foo")
  .Select(f => new {...})
  .ToList();   

这很好,因为在我们将数据实现为List(匿名类型)之前,我们已经应用了谓词和SQL中我们需要的列的投影

行:

var foos = myDbContext.MyFooSet
  .Where(f => f.SomeProperty = "foo")
  .ToList() // Or .AsEnumerable(), or other materialization methods
  .Select(f => new {...}); // Subset of fields

这不是理想的,因为虽然我们在实现之前已经应用了.Where子句,但我们仍然会返回Foo表中的完整列,只是为了丢弃不必要的列。这意味着不必要的I / O,而且,Sql可能只能使用索引来执行查询。

糟糕 - 从不这样做

var foos = myDbContext.MyFooSet
  .AsEnumerable() // (or `ToList()`, same problem)
  .Where(f => f.SomeProperty = "foo")
  .Select(f => new {...}); // Subset of fields

这似乎是OP正在经历的事情 - 因为在进行任何.Where过滤之前表已实现,所以整个表被检索到内存并且使用Linq对象进行过滤。

在应用不使用.Where的自定义Expressions谓词构建器,或者使用IEnumerable<T>代替IQueryable<T> - {{1>时,也会出现此问题没有关联的表达式树,无法解析为Sql。