57 votes

Pourquoi EF génère-t-il des requêtes SQL avec des vérifications nulles inutiles?

Je suis tombé sur un problème avec EF création terrible requêtes lors de la recherche sur un champ de type chaîne. Son produit une requête dans le style de programmeurs paresseux pour englober null contrôle des forces de la totalité de l'index pour être analysés.

examiner les requêtes suivantes.

  1. Requête 1

    var p1 = "x";
    var r1 = ctx.Set<E>().FirstOrDefault(
                            subject =>
                                p1.Equals(subject.StringField));
    
  2. Requête 2

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

Requête 1 produit

WHERE (('x' = [Extent2].[StringField]) OR (('x' IS NULL) AND ([Extent2].[StringField] IS NULL))) 

et s'exécute en 4 secondes

Requête 2 produit

WHERE (N'x' = [Extent2].[StringField]) 

et s'exécute en 2 millisecondes

Quelqu'un sait-il de solutions? (pas de paramètre ne peut pas être un const tel qu'il est saisi par l'utilisateur, mais ne peut pas être null.)

N. B Quand les profilés, les deux requêtes sont préparés avec sp_executesql par EF; comme de cause s'ils étaient juste exécuté la requête de l'optimiseur va à l'encontre du OU " x " EST NULL vérifier.

for @Martin

45voto

Bassam Alugili Points 1958

Ensemble UseDatabaseNullSemantics = true;

Obtient ou définit une valeur indiquant si la base de données sémantique est null exposé lors de la comparaison des deux opérandes, qui sont potentiellement nullable. La valeur par défaut est false. Par exemple (opérande1 == opérande2) sera traduit comme: (opérande1 = opérande2) si UseDatabaseNullSemantics est vrai, respectivement (((opérande1 = opérande2) ET (NON (opérande1 EST NULL OU opérande2 EST NULL))) OU ((opérande1 EST NULL) ET (opérande2 EST NULL)))) si UseDatabaseNullSemantics est faux.

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

public class MyContext : DbContext
{
    public MyContext()
    {
        this.Configuration.UseDatabaseNullSemantics = true;
    }
}

Vous pouvez également définir ce paramètre à votre instance dbContext de l'extérieur comme de l'exemple de code ci-dessous, de mon point de vue(voir @GertArnold commentaire), ce apporach sera mieux, car il ne sera pas modifier la valeur par défaut de la base de données de comportement ou de configuration):

myDbContext.Configuration.UseDatabaseNullSemantics = true;

9voto

Kahbazi Points 5592

Vous pouvez résoudre ce problème en ajoutant [Required] sur StringField propriété

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));

c'est requête1

{SÉLECTIONNEZ [Extent1].[Id] COMME [Id], [Extent1].[Bar] COMME [Bar], [Extent1].[Foo] COMME [Foo] À PARTIR de [dbo].[Tests] COMME [Extent1] OÙ @p__linq__0 = [Extent1].[Bar]}

et c'est query2

{SÉLECTIONNEZ [Extent1].[Id] COMME [Id], [Extent1].[Bar] COMME [Bar], [Extent1].[Foo] COMME [Foo] À PARTIR de [dbo].[Tests] COMME [Extent1] OÙ (@p__linq__0 = [Extent1].[Foo]) OU ((@p__linq__0 EST NULLE) ET ([Extent1].[Bar2] EST NULL))}

3voto

Mark Points 186

Un de mes collègues a juste trouvé un vraiment vraiment sympa solution. Depuis que j'ai déjà découvert que l'utilisation de constantes produit le SQL correcte. Nous nous sommes demandé si nous pouvions échanger les variables dans l'expression de constantes, et il s'avère que vous le pouvez. Je crois que cette méthode soit moins invasive que la modification de la valeur null paramètres sur la DB contexte.

public class Foo_test : EntityContextIntegrationSpec
        {

            private static string _foo = null;

            private static DataConnection _result;

            private Because _of = () => _result = EntityContext.Set<E>().Where(StringMatch<E>(x => x.StringField));

            private static Expression<Func<TSource, bool>> StringMatch<TSource>(Expression<Func<TSource, string>> prop)
            {
                var body = Expression.Equal(prop.Body, Expression.Constant(_foo));
                return Expression.Lambda<Func<TSource,bool>>(body, prop.Parameters[0]);                
            }

            [Test] public void Test() => _result.ShouldNotBeNull();
        }

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X