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 语句的确切内容 运行,以及这些查询的性能影响和正在处理的数据量返回。
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 语句的确切内容 运行,以及这些查询的性能影响和正在处理的数据量返回。