C# EF如何使用LINQ`Where`函数中提供的回调从DB中选择元素?

C# EF如何使用LINQ`Where`函数中提供的回调从DB中选择元素?,c#,database,entity-framework,linq,predicate,C#,Database,Entity Framework,Linq,Predicate,EF如何使用LINQWhere函数中提供的回调从DB中选择元素 例如,如果我有一个查询context.People.Where(p=>isOldEnough(p)),这是否意味着EF将查询数据库中的所有人,然后对他们应用谓词并返回剩余的结果,或者谓词将以某种方式转换为实际的数据库查询 我知道,只要context.People.Where(p=>isOldEnough(p))的结果以某种方式被使用(例如,在迭代或cast中),就会发生对DB的实际查询 我在互联网上找不到这些信息,所以我决定在这里提

EF如何使用LINQ
Where
函数中提供的回调从DB中选择元素

例如,如果我有一个查询
context.People.Where(p=>isOldEnough(p))
,这是否意味着EF将查询数据库中的所有人,然后对他们应用谓词并返回剩余的结果,或者谓词将以某种方式转换为实际的数据库查询

我知道,只要
context.People.Where(p=>isOldEnough(p))
的结果以某种方式被使用(例如,在迭代或cast中),就会发生对DB的实际查询


我在互联网上找不到这些信息,所以我决定在这里提问。

对于EF6,表达方式是:

var oldEnoughPeople = context.People.Where(p => isOldEnough(p));
其中,
isOldEnough
是代码库中的一个方法,它将抛出一个异常,EF无法将
isOldEnough
转换为SQL。解决这个问题的简单方法是插入一个
ToList()
,以强制EF计算Linq2Object中的表达式:

var oldEnoughPeople = context.People.ToList().Where(p => isOldEnough(p));
直接的问题是,EF将在应用过滤器之前将数据库中的所有人员都提取到应用程序服务器上的内存中。这可能非常昂贵,尤其是在一个web应用程序中,使用一个用户会话进行测试不会突出问题,但在生产中,数百个请求进入,事情会陷入停顿

如果
isOldEnough()
只是在做一些逻辑,比如比较Person.Age>=18,然后将其移到Where子句中:

var oldEnoughPeople = context.People.Where(p => p.Age >= 18);
这允许EF将表达式转换为SQL并将其传递给数据库。返回的唯一数据将是适用的人员记录

现在,当谈到EF核心时,开发人员已经击落了一颗致命的地雷(IMHO),当EF遇到它无法翻译的原始表达式时,它将有效地自动将
.ToList()
打进去,而不是抛出异常。我相信它确实发出了警告,表明它已经这样做了,但除此之外,这可能是一个相当安静的性能陷阱,您应该注意。然而,至少对于EF Core来说,其可取之处在于它可以转换为SQL的任何查询表达式都将首先应用于SQL

例如,给定以下表达式:

var oldEnoughPeopleStartingWithS = context.People.Where(p => p.Name.StartsWith("s") && isOldEnough(p));
由于EF可以将
string.StartsWith
转换为SQL,因此EF Core将执行一个查询,以选择名称以“s”开头的所有人员。这些实体将被具体化,并从中应用内存中的isOldEnough检查。但是,这需要仔细考虑,因为如果这是一个OR操作(
p.Name.StartsWith(“s”)| | isOldEnough(p)
),EF将在筛选之前有效地获取所有个人记录,就像前面的
.ToList()
示例一样


在使用EF进行开发时,我强烈建议对数据库使用探查器,以准确检查正在运行的SQL语句,以及这些查询的性能影响和返回的数据量。

。否则,它会将您的查询转换为正确的SQL。类似于从isOldEnough(p)所在的人中选择*。请参阅。@HimBromBeere,查询是否会存储在
上下文中的某个位置?您没有指定使用的EF版本,但您可能会发现本文也很有用:在这里您可以实际看到查询生成的SQL。在较高的级别上,
context.People。其中(p=>isOldEnough(p))
是IQueryable,您可以将其视为上下文顶部的表达式树。在您尝试迭代它时(而不是之前),它会将表达式树转换为SQL语句,将其发送到数据库,执行它并获得结果。在您的示例中,它尝试使用
isOldEnough
来过滤记录,除非返回EF可以转换为SQL的内容,否则您将收到一个异常,表明它无法将其转换为SQL。