EF 如何将 LINQ `Where` 函数中提供的回调用于来自 DB 的 select 元素?

How does the EF use the callback provided in the LINQ `Where` function to select elements from DB?

EF 如何将 LINQ Where 函数中提供的回调用于数据库中的 select 个元素?

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

我知道一旦 context.People.Where(p => isOldEnough(p)) 的结果以某种方式使用(例如在迭代或强制转换中),就会对数据库进行实际查询。

我无法在 Internet 上找到该信息,所以我决定在这里问一下。

使用 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 会从数据库中提取 所有 人到应用程序服务器的内存中。这可能非常昂贵,尤其是在使用 1 个用户会话进行测试不会突出问题的 Web 应用程序中,但在有数百个请求进入的生产中,事情就会停止。

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

var oldEnoughPeople = context.People.Where(p => p.Age >= 18);

这允许 EF 将表达式转换为 SQL 并将其传递给数据库。返回的唯一数据将是适用的人员记录。

现在,当谈到 EF Core 时,开发人员已经埋下了一个危险的地雷(恕我直言),因为当 EF 遇到它无法翻译的原始表达式时,它会有效地打击 .ToList() 自动而不是抛出异常。我相信它确实会发出警告,表明它已经这样做了,但除此之外,这可能是一个相当隐蔽的性能缺陷,您应该注意这一点。但是,至少对于 EF Core,可取之处在于它可以转换为 SQL 的任何查询表达式都将首先应用于 SQL。

例如,给定以下表达式:

var oldEnoughPeopleStartingWithS = context.People.Where(p => p.Name.StartsWith("s") && isOldEnough(p));

EF Core 将对 select 所有名字以 "s" 开头的人执行查询,因为 EF 可以将 string.StartsWith 翻译成 SQL。这些实体将被具体化,并从中应用内存中的 isOldEnough 检查。但是,这需要仔细考虑,因为如果这是一个 OR 操作 (p.Name.StartsWith("s") || isOldEnough(p)),EF 会在过滤之前有效地获取 all 个人记录,就像之前的 .ToList() 例子。

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