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?
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; }
...
}
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.
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
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.
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 .
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.
Vous pouvez utiliser les astuces suivantes pour accélérer vos requêtes -
ctx.Configuration.ProxyCreationEnabled
Sur false
juste avant que vous obteniez le contexte..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é.