104 votes

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

Je rencontre des problèmes de performance vraiment étranges liés à une requête très simple utilisant Entity Framework Code-First avec le framework .NET version 4. La requête LINQ2Entities ressemble à ceci :

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

Cela prend plus de 3000 millisecondes pour s'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 change le code C# pour utiliser la fonction SqlQuery, elle s'exécute en 5 à 10 millisecondes :

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

Ainsi, même SQL, les entités résultantes sont suivies dans les deux cas, mais il y a une grande différence de performance entre les deux. Qu'est-ce qui se passe ?

2 votes

Je suppose que vous rencontrez des retards d'initialisation - probablement la compilation de la vue. Voir MSDN: Considérations de performance pour Entity Framework 5

0 votes

J'ai essayé de pré-générer les vues, et cela ne semble pas aider. De plus, j'ai exécuté une autre requête EF avant la lente pour exclure les trucs d'initialisation. La nouvelle requête s'est exécutée rapidement, la lente s'est encore exécutée lentement, même si la préparation du contexte s'est déroulée lors de la première requête.

1 votes

@marc_s - Non, SqlQuery renverra une instance d'entité entièrement matérialisée et suivie des changements. Voir msdn.microsoft.com/en-us/library/…

3voto

user2622095 Points 1

J'ai également rencontré cela avec une requête ef complexe. Une solution pour moi qui a réduit une requête ef de 6 secondes à la requête sql subsecondaire générée était de désactiver le chargement différé.

Pour trouver ce paramètre (ef 6), allez dans le fichier .edmx et regardez dans Propriétés -> Génération de code -> Chargement différé activé. Mettez à false.

Amélioration massive des performances pour moi.

4 votes

C'est cool, mais ça n'a rien à voir avec la question de l'affiche.

2voto

SoItBegins Points 369

Voici un bug EF très étrange qui m'est arrivé et qui est une raison de plus pour que cela puisse mal tourner.

J'avais la ligne de code suivante:

var fvQuery = db.DataValues.Where(x => x.DataId == dataId && x.Scope == scope && x.Start <= endTime && startTime <= x.End)

Remarquez que la dernière partie de la clause Where a les termes inversés, avec la valeur comparée à gauche!

Cela a presque toujours fonctionné, puis a buggé dans des circonstances rares, la requête prenant plusieurs minutes dans Entity Framework avant d'être envoyée à la base de données, si elle l'était.

La solution:

var fvQuery = db.DataValues.Where(x => x.DataId == dataId && x.Scope == scope && x.Start <= endTime && x.End >= startTime)

Échanger l'ordre des termes dans la dernière partie de la clause WHERE semble l'avoir corrigé.

0voto

Erik123 Points 201

Dans mon cas, c'était une impasse causée par une méthode appelante qui a oublié d'attendre ma méthode asynchrone. Le query.ToArrayAsync() a été exécuté mais n'est jamais retourné au thread appelant.

0voto

tim_p Points 31

J'ai eu une expérience similaire impossible à résoudre avec d'autres réponses ici et j'ai opté pour la requête SQL directe en dehors de l'EF

0voto

SoItBegins Points 369

Cela m'est arrivé à nouveau dans une partie similaire du code sur laquelle je travaillais (voir ma réponse précédente pour savoir comment j'ai résolu le problème la première fois).

Cette fois, le coupable était l'optimiseur de requêtes qui oubliait d'utiliser un index. Lors de l'exécution de la requête dans la plupart des cas, ou sous SSMS, les bons index étaient utilisés. Seulement de manière occasionnelle, dans des circonstances spécifiques, cela échouait.

En utilisant un profiler SQL et en activant l'affichage des plans d'exécution, j'ai vu que dans les requêtes longues, l'optimiseur de requêtes ignorait l'index qui avait été défini et effectuait plutôt un balayage de table (de plus de 6 millions de lignes!), augmentant considérablement le temps d'exécution.

La solution à ce problème a été de transformer la requête en une fonction à valeur de table avec un indice hint, forçant l'utilisation du bon index, et d'appeler cette fonction depuis EF. Cela a résolu le problème, bien que ce soit une solution quelque peu drastique. Je ne sais pas pourquoi l'optimiseur a décidé d'ignorer l'index, mais cela garantit qu'il ne le fera pas à l'avenir.

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