实体框架查询未命中过滤索引WHERE BIT字段= 0

Sco*_*ner 5 c# sql t-sql linq-to-entities entity-framework

我注意到Entity Framework使用负布尔过滤器转换LINQ查询,这样生成的查询计划就不会使用过滤索引.例如,查询:

context.Foo.Count(f => !f.IsActive)
Run Code Online (Sandbox Code Playgroud)

生成SQL语句:

SELECT 
    [GroupBy1].[A1] AS [C1]
    FROM ( SELECT 
        COUNT(1) AS [A1]
        FROM [dbo].[Foos] AS [Extent1]
        WHERE [Extent1].[IsActive] <> cast(1 as bit)
    )  AS [GroupBy1]
Run Code Online (Sandbox Code Playgroud)

请注意该WHERE子句使用[IsActive] <> cast(1 as bit),而不是更直观[IsActive] = 0.使用筛选索引时,这会成为问题.上述查询的计划不会使用以下索引:

CREATE INDEX IX_Foo_IsActive ON Foos (IsActive) WHERE (IsActive = 0)
Run Code Online (Sandbox Code Playgroud)

我怀疑EF以这种方式生成查询的原因与DB null语义有关,但即使使用不可空的位字段也会发生这种情况.我已经验证用EF的语法(IsActive <> 1)编写过滤的索引可以解决问题,但是这会破坏使用更常见语法的任何非EF查询.

有更好的解决方法吗?

这里有完整的示例程序:http://dotnetfiddle.net/3kZugt.上面使用的实体类型是:

public class Foo
{
    public int Id { get; set; }
    public bool IsActive { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

Jot*_*aBe 6

出于某种奇怪的原因,有时候我们看不到一些非常明显的东西并不常见:将数据库谓词直接转换为C#谓词,即

WHERE IsActive = 0
Run Code Online (Sandbox Code Playgroud)

被翻译成

f => f.IsActive = false
Run Code Online (Sandbox Code Playgroud)

你必须停止在C#中思考并开始在SQL中思考;)

  • 实际上有一个解释:L2E解析lambdas的表达式树,并在可能的情况下将它们逐字地转换为SQL.所以表达式树"f.IsActive = false"从字面上转换为"0 = IsActive".并且您的原始表达式`f =>!f.IsActive`没有直接转换为db,因此在将树转换为SQL之前对其进行操作.我想它在内部必须将`f =>!f.IsActive`的表达式树转换为`f => true!= f.IsActive`才能将其转换为SQL.相信我,这是最奇怪的一切. (2认同)