为什么EF使用不必要的空值检查生成SQL查询?

Mar*_*ark 47 c# sql-server entity-framework

我在搜索字符串字段时遇到了EF创建可怕查询的问题.它以懒惰程序员的风格产生了一个查询,包含空检查,强制扫描整个索引.

考虑以下查询.

  1. 查询1

    var p1 = "x";
    var r1 = ctx.Set<E>().FirstOrDefault(
                            subject =>
                                p1.Equals(subject.StringField));
    
    Run Code Online (Sandbox Code Playgroud)
  2. 查询2

    const string p2 = "x";
    var r2 = ctx.Set<E>().FirstOrDefault(
                            subject =>
                                p2.Equals(subject.StringField));
    
    Run Code Online (Sandbox Code Playgroud)

查询1生成

WHERE (('x' = [Extent2].[StringField]) OR (('x' IS NULL) AND ([Extent2].[StringField] IS NULL))) 
Run Code Online (Sandbox Code Playgroud)

并在4秒内执行

查询2生成

WHERE (N'x' = [Extent2].[StringField]) 
Run Code Online (Sandbox Code Playgroud)

并在2毫秒内执行

有谁知道任何工作?(没有参数不能是const,因为它是由用户输入输入的,但不能为null.)

NB在分析时,两个查询都是由EF用sp_executesql编写的; 因为如果它们刚被执行,查询优化器将否定OR'x'IS NULL检查.

为@Martin

Bas*_*ili 36

设置UseDatabaseNullSemantics = true;

获取或设置一个值,该值指示在比较两个操作数时是否显示数据库空语义,这两个操作数都可能为空.默认值为false.例如(operand1 == operand2)将被翻译为:(operand1 = operand2)如果UseDatabaseNullSemantics分别为true(((operand1 = operand2)AND(NOT(operand1 IS NULL或operand2 IS NULL)))OR((operand1 IS) NULL)AND(operand2 IS NULL)))如果UseDatabaseNullSemantics为false.

https://msdn.microsoft.com/en-us/library/system.data.entity.infrastructure.dbcontextconfiguration.usedatabasenullsemantics(v=vs.113).aspx

WHERE operand1 = operand2
Run Code Online (Sandbox Code Playgroud)

你也可以从外部设置这个设置到你的dbContext实例,就像下面的代码示例一样,从我的角度来看(参见@GertArnold评论),这个apporach会更好,因为它不会改变默认的数据库行为或配置):

WHERE
    (
        (operand1 = operand2)
        AND
        (NOT (operand1 IS NULL OR operand2 IS NULL))
    )
    OR
    (
        (operand1 IS NULL)
        AND
        (operand2 IS NULL)
    )
Run Code Online (Sandbox Code Playgroud)


Kah*_*azi 8

您可以通过添加[Required]StringField属性来解决此问题

public class Test
{
    [Key]
    public int Id { get; set; }
    [Required]
    public string Bar{ get; set; }
    public string Foo { get; set; }

}


 string p1 = "x";
 var query1 = new Context().Tests.Where(F => p1.Equals(F.Bar));

 var query2 = new Context().Tests.Where(F => p1.Equals(F.Foo));
Run Code Online (Sandbox Code Playgroud)

这是query1

{SELECT [Extent1].[Id] AS [Id],[Extent1].[Bar] AS [Bar],[Extent1].[Foo] AS [Foo] FROM [dbo].[Tests] AS [Extent1] WHERE @ p__linq__0 = [Extent1].[Bar]}

这是query2

{SELECT [Extent1].[Id] AS [Id],[Extent1].[Bar] AS [Bar],[Extent1].[Foo] AS [Foo] FROM [dbo].[Tests] AS [Extent1] WHERE (@ p__linq__0 = [Extent1].[Foo])OR((@ p__linq__0 IS NULL)AND([Extent1].[Bar2] IS NULL))}