web-dev-qa-db-fra.com

Requête Entity Framework lente, mais le même SQL dans SqlQuery est rapide

Je vois des performances très étranges liées à une requête très simple utilisant Entity Framework Code-First avec .NET Framework version 4. La requête LINQ2Entities ressemble à ceci:

 context.MyTables.Where(m => m.SomeStringProp == stringVar);

Cela prend plus de 3000 millisecondes à exécuter. Le SQL généré semble très simple:

 SELECT [Extent1].[ID], [Extent1].[SomeStringProp], [Extent1].[SomeOtherProp],
 ...
 FROM [MyTable] as [Extent1]
 WHERE [Extent1].[SomeStringProp] = '1234567890'

Cette requête s'exécute presque instantanément lorsqu'elle est exécutée via Management Studio. Lorsque je modifie le code C # pour utiliser la fonction SqlQuery, il s'exécute en 5 à 10 millisecondes:

 context.MyTables.SqlQuery("SELECT [Extent1].[ID] ... WHERE [Extent1].[SomeStringProp] = @param", stringVar);

Ainsi, exactement le même code SQL, les entités résultantes font l’objet d’un suivi des modifications dans les deux cas, mais il existe une différence sauvage entre les deux. Ce qui donne?

77
Brian Sullivan

Je l'ai trouvé Il s'avère que c'est une question de types de données SQL. La colonne SomeStringProp de la base de données était un varchar, mais EF suppose que les types de chaînes .NET sont nvarchars. Le processus de traduction résultant de la requête de la base de données pour la comparaison prend beaucoup de temps. Je pense qu'EF Prof m'avait un peu égaré ici, une représentation plus précise de la requête en cours d'exécution serait la suivante:

 SELECT [Extent1].[ID], [Extent1].[SomeStringProp], [Extent1].[SomeOtherProp],
 ...
 FROM [MyTable] as [Extent1]
 WHERE [Extent1].[SomeStringProp] = N'1234567890'

Le correctif résultant consiste donc à annoter le modèle code-first en indiquant le type de données SQL correct:

public class MyTable
{
    ...

    [Column(TypeName="varchar")]
    public string SomeStringProp { get; set; }

    ...
}
79
Brian Sullivan

La raison du ralentissement de mes requêtes dans EF consistait à comparer des scalaires non nullables avec des scalaires nullables:

long? userId = 10; // nullable scalar

db.Table<Document>().Where(x => x.User.Id == userId).ToList() // or userId.Value
                                ^^^^^^^^^    ^^^^^^
                                Type: long   Type: long?

Cette requête a pris 35 secondes. Mais un petit refactoring comme ça:

long? userId = 10;
long userIdValue = userId.Value; // I've done that only for the presentation pursposes

db.Table<Document>().Where(x => x.User.Id == userIdValue).ToList()
                                ^^^^^^^^^    ^^^^^^^^^^^
                                Type: long   Type: long

donne des résultats incroyables. Il n'a fallu que 50 ms pour terminer. Il est possible que ce soit un bug dans EF.

36
cryss

Si vous utilisez le mappage fluide, vous pouvez utiliser IsUnicode(false) dans le cadre de la configuration pour obtenir le même effet -

http://msdn.Microsoft.com/en-us/data/jj591617.aspx#1.9

http://msdn.Microsoft.com/en-us/library/gg696416%28v=vs.103%29.aspx

7
Matt

J'ai eu le même problème (la requête est rapide lorsqu'il est exécuté à partir du gestionnaire SQL), mais lorsqu'il est exécuté à partir de EF, le délai d'expiration expire.

Il s'avère que l'entité (qui a été créée à partir de la vue) avait de mauvaises clés d'entité. Ainsi, l'entité avait des lignes en double avec les mêmes clés et je suppose qu'elle devait regrouper sur l'arrière-plan.

3
Vladimir Gedgafov

J'ai eu ce problème aussi. Il s’est avéré que le coupable dans mon cas était SQL-Server recherche de paramètre .

Le premier indice que mon problème était en fait dû au reniflage de paramètre était que l'exécution de la requête avec "set arithabort off" ou "set arithabort on" produisait des temps d'exécution radicalement différents dans Management Studio. En effet, ADO.NET utilise par défaut "set arithabort off" et par défaut, Management Studio "set arithabort on". Le cache du plan de requête conserve différents plans en fonction de ce paramètre.

J'ai désactivé la mise en cache du plan de requête pour la requête, avec la solution que vous pouvez trouver ici .

2
Oskar Sjöberg

Je suis également tombé sur cela avec une requête ef complexe. Un correctif pour moi qui réduisait une requête ef de 6 secondes à la sous-seconde requête SQL générée était de désactiver le chargement différé.

Pour trouver ce paramètre (exemple 6), accédez au fichier .edmx et recherchez-le dans Propriétés -> Génération de code -> Chargement différé activé. Défini sur false.

Amélioration massive des performances pour moi.

2
user2622095

Vous pouvez utiliser les astuces suivantes pour accélérer vos requêtes -

  1. Définissez ctx.Configuration.ProxyCreationEnabled Sur false juste avant que vous obteniez le contexte.
  2. De plus, .Select(c => new {c.someproperty}) ne récupérera que les données requises et non le lot complet.

Faites-moi savoir si cela a aidé.

1
Rajesh Panda