Pourquoi Entity Framework prend-il 30 secondes pour charger des enregistrements alors que la requête générée ne prend que 1/2 seconde?

ExecuteTime ci-dessous correspond à 30 secondes la première fois et à 25 secondes la prochaine fois que j’exécute le même ensemble de code. Lorsque je regarde dans SQL Profiler, je vois tout de suite un identifiant de connexion, puis il rest là pendant environ 30 secondes. Ensuite, dès que l’instruction select est exécutée, l’application termine la commande ToList. Lorsque j’exécute la requête générée à partir de Management Studio, la requête de firebase database ne prend qu’environ 400 ms. Il retourne 14 lignes et 350 colonnes. Il semble que le temps nécessaire à la transformation des résultats de la firebase database en entités soit si petit qu’il n’est pas perceptible.

Alors que se passe-t-il dans les 30 secondes précédant l’appel de la firebase database?

Si le cadre de l’entité est aussi lent, il ne nous est pas possible de l’utiliser. Y a-t-il quelque chose que je fais mal ou que je puisse changer pour accélérer le processus?

UPDATE: D’accord, si j’utilise une requête compilée, la première fois que cela prend 30 secondes, et la deuxième fois, cela prend 1/4 de seconde. Est-ce que je peux faire quelque chose pour accélérer le premier appel?

using (EntitiesContext context = new EntitiesContext()) { Stopwatch sw = new Stopwatch(); sw.Start(); var groupQuery = (from g in context.Groups.Include("DealContract") .Include("DealContract.Contracts") .Include("DealContract.Contracts.AdvertiserAccountType1") .Include("DealContract.Contracts.ContractItemDetails") .Include("DealContract.Contracts.Brands") .Include("DealContract.Contracts.Agencies") .Include("DealContract.Contracts.AdvertiserAccountType2") .Include("DealContract.Contracts.ContractProductLinks.Products") .Include("DealContract.Contracts.ContractPersonnelLinks") .Include("DealContract.Contracts.ContractSpotOrderTypes") .Include("DealContract.Contracts.Advertisers") where g.GroupKey == 6 select g).OfType(); sw.Stop(); var queryTime = sw.Elapsed; sw.Reset(); sw.Start(); var groups = groupQuery.ToList(); sw.Stop(); var executeTime = sw.Elapsed; } 

J’ai eu exactement le même problème, ma requête prenait 40 secondes.

J’ai trouvé que le problème .Include("table_name") fonctions .Include("table_name") . Plus j’en avais, plus c’était pire. Au lieu de cela, j’ai remplacé mon code par Lazy Load, après la requête, toutes les données dont j’avais besoin, ce qui a réduit le temps total à environ 1,5 seconde au lieu de 40 secondes. Autant que je sache, ceci accomplit exactement la même chose.

Donc, pour votre code, ce serait quelque chose comme ça:

 var groupQuery = (from g in context.Groups where g.GroupKey == 6 select g).OfType(); var groups = groupQuery.ToList(); foreach (var g in groups) { // Assuming Dealcontract is an Object, not a Collection of Objects g.DealContractReference.Load(); if (g.DealContract != null) { foreach (var d in g.DealContract) { // If the Reference is to a collection, you can just to a Straight ".Load" // if it is an object, you call ".Load" on the refence instead like with "g.DealContractReference" above d.Contracts.Load(); foreach (var c in d.Contracts) { c.AdvertiserAccountType1Reference.Load(); // etc.... } } } } 

Incidemment, si vous deviez append cette ligne de code au-dessus de la requête dans votre code actuel, le temps serait ramené à environ 4 à 5 secondes (encore trop longtemps dans mon option). De ce que je comprends, l’option MergeOption.NoTracking désactivée. une grande partie de la surcharge de suivi liée à la mise à jour et à l’insertion d’éléments dans la firebase database:

 context.groups.MergeOption = MergeOption.NoTracking; 

C’est à cause de l’inclusion. J’imagine que vous avez hâte de charger beaucoup d’objects dans la mémoire. Il faut beaucoup de temps pour créer les objects c # qui correspondent à vos entités de firebase database.

Ma recommandation pour vous est d’essayer de charger paresseux uniquement les données dont vous avez besoin.

Le seul moyen que je connaisse pour accélérer la compilation initiale de la requête est de la rendre moins complexe. La documentation MSDN sur les considérations de performances pour Entity Framework et les requêtes compilées n’indique pas qu’il est possible d’enregistrer une requête compilée pour l’utiliser dans une session d’exécution d’application différente.

J’appendais que nous avons constaté qu’avoir beaucoup d’inclusions pouvait rendre l’exécution de la requête plus lente que d’avoir moins d’inclusions et faire plus de charges sur des entités associées ultérieurement. Quelques essais et erreurs sont nécessaires pour trouver le bon support.

Cependant, je dois vous demander si vous avez vraiment besoin de chaque propriété de chaque entité que vous incluez ici. Il me semble qu’il existe un grand nombre de types d’entités différents dans cette requête; leur matérialisation pourrait donc coûter assez cher. Si vous essayez simplement d’obtenir des résultats tabulaires que vous n’avez pas l’intention de mettre à jour, projetez le nombre (relativement) réduit de champs dont vous avez réellement besoin dans un type plat et anonyme devrait être considérablement plus rapide pour diverses raisons. En outre, cela vous évite d’avoir à vous soucier du chargement, de l’appel de Load / IsLoaded, etc.

Vous pouvez certainement accélérer la génération de vues initiale en précompilant les vues d’entités. Il existe une documentation sur MSDN pour cela . Mais comme vous payez ce coût au moment de l’exécution de la première requête, votre test avec une requête simple montre que celle-ci s’exécute dans le voisinage de 2 secondes. C’est bien de dire que 2 secondes, mais cela ne sauvera rien d’autre.

EF met un certain temps à démarrer. Il a besoin de construire des métadonnées à partir de XML et génère probablement des objects utilisés pour le mappage. Il faut donc quelques secondes pour démarrer, je ne pense pas qu’il existe un moyen de contourner cela, à moins de ne jamais redémarrer votre programme.