为啥 Entity Framework 6 会为简单的查找生成复杂的 SQL 查询?

Posted

技术标签:

【中文标题】为啥 Entity Framework 6 会为简单的查找生成复杂的 SQL 查询?【英文标题】:Why does Entity Framework 6 generate complex SQL queries for simple lookups?为什么 Entity Framework 6 会为简单的查找生成复杂的 SQL 查询? 【发布时间】:2013-12-05 02:23:09 【问题描述】:

我有这个 LINQ 查询

dbContext.Customers.Where(c => c.AssetTag == assetTag).Count();

(from c in dbContext.Customers
 where c.AssetTag == assetTag
 select c).Count();

生成的SQL是

SELECT 
[GroupBy1].[A1] AS [C1]
FROM ( SELECT 
    COUNT(1) AS [A1]
    FROM [dbo].[Customer] AS [Extent1]
    WHERE (([Extent1].[AssetTag] = @p__linq__0) AND ( NOT ([Extent1].[AssetTag] IS NULL    OR @p__linq__0 IS NULL))) OR (([Extent1].[AssetTag] IS NULL) AND (@p__linq__0 IS NULL))
)  AS [GroupBy1]

那么,为什么 LINQ 会为一个简单的 where 语句生成如此复杂的 SQL?

【问题讨论】:

您的[AssetTag] 列是否允许为null?。 对我来说似乎并不复杂,它只是创建一个语句来保护您的呼叫免受空范围的影响 您能告诉我如何从 Linq 或 Lamda 获取此 SQL 查询吗?这与您的问题完全无关,但我无法获得。 @e10 您可以使用 ToTraceString 方法 (msdn.microsoft.com/en-us/library/…) 或 LINQPad 之类的工具。 【参考方案1】:

在 C# 字符串等效项中,null == null 的计算结果为 True。数据库中的null == null 计算为False。该脚本正在验证列值和参数是否都为空,或者两者都不为空并且它们具有相同的字符串值。

WHERE 
    (
        -- neither the column nor the paramter are null and
        -- the column and the parameter have the same string value
        ([Extent1].[AssetTag] = @p__linq__0) AND 
        ( NOT ([Extent1].[AssetTag] IS NULL    OR @p__linq__0 IS NULL))
    ) 
    OR 
    (
        -- both the column value and the parameter are null
        ([Extent1].[AssetTag] IS NULL) AND 
        (@p__linq__0 IS NULL)
    )

【讨论】:

谢谢大家的解释【参考方案2】:

在 EF6 中,数据库空语义是默认的比较语义。请注意,这是对 EF5 中默认设置的更改。在 EF5 中,该标志隐藏在 ObjectContext.ContextOptions.UseCSharpNullComparisonBehavior 中,默认情况下 EF 将使用 Linq 到 Object 比较语义。在 EF6 中,它在 DbContext 上公开为 DbContext.Configuration.UseDatabaseNullSemantics。您可以找到更多详情here

【讨论】:

您链接的 msdn 帮助页面显示 DbContextConfiguration.UseDatabaseNullSemantics 的默认值为 false ——因此在 EF6 数据库中,空语义不是默认比较语义?【参考方案3】:

WHERE 条件是这样生成的,因为使用 ANSI NULLS 设置,比较 AssetTag == null 将不会返回 SQL 中的相应行(因为在 SQL 世界中,当比较 null 与 null 时,结果为 null)。为了使查询行为与 C# 开发人员期望的相同,EF 生成扩展的 WHERE 子句。请注意,早期版本的 EF 没有这样做,因此不适用于具有 ANSI NULLS 设置的数据库。

之所以存在 GroupBy 投影,是因为 EF 在 .Count() 调用之前支持更复杂的查询,例如连接、投影等。因此这种方法更通用,因为它也适用于所有这些场景。

【讨论】:

本来想解释一下范围的,虽然了解的很肤浅,但是不知道怎么解释。 +1 说得如此优雅。 +1 提到 6 之前的 EF 这样做。【参考方案4】:

一方面,在 C# 中,c.AssetTag == assetTag 如果两者都为空,则为真。然而,在 SQL 中,与任何事物相比,null 总是错误的。因此,如果我们想要生成一个遵循 C# 比较机制的查询,我们必须添加额外的条件来确保 null 比较在两者都为 null 时评估为 true:

([Extent1].[AssetTag] IS NULL) AND (@p__linq__0 IS NULL)

【讨论】:

以上是关于为啥 Entity Framework 6 会为简单的查找生成复杂的 SQL 查询?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 Entity framework Entity Master Details Entity Edit

为啥我的 Entity Framework Code First 代理集合为空,为啥我不能设置它?

为啥在我运行迁移时 Entity Framework 包会自动更新?

为啥我必须为 Code First / Entity Framework 提供无参数构造函数

为啥Entity Framework没有在使用SingleOrDefault时生成的SQL中添加“where”?

使用 MySQL 时,为啥 Entity Framework 4 试图将 long 转换为小数?