有一件事让我感到困惑。我认为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在表中发布了错误。
实体框架是否首先将所有记录提取到内存并过滤后?
答案 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中我们需要的列的投影1} p>
行:
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。