2009-03-26 8 views
16

Le délai d'exécution ci-dessous est de 30 secondes la première fois et de 25 secondes la prochaine fois que j'exécute le même jeu de code. Lorsque vous regardez dans SQL Profiler, je vois immédiatement une connexion, puis il reste 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 base de données ne prend que 400 ms. Il renvoie 14 lignes et 350 colonnes. Il semble que le temps qu'il faut pour transformer les résultats de la base de données en entités est si petit qu'il n'est pas perceptible.Pourquoi Entity Framework prend-il 30 secondes pour charger des enregistrements lorsque la requête générée ne prend qu'une demi-seconde?

Alors ce qui se passe dans les 30 secondes avant que l'appel de la base de données est faite?

Si le framework d'entité est aussi lent, il ne nous est pas possible de l'utiliser. Y at-il quelque chose que je fais mal ou quelque chose que je peux changer pour accélérer cela de façon spectaculaire?

MISE À JOUR: D'accord, si j'utilise une requête Compilé, la première fois qu'il prend 30 secondes, et la deuxième fois qu'il faut 1/4 d'une seconde. Y a-t-il quelque chose que je puisse faire 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<Deal>(); 
    sw.Stop(); 
    var queryTime = sw.Elapsed; 
    sw.Reset(); 
    sw.Start(); 
    var groups = groupQuery.ToList(); 
    sw.Stop(); 
    var executeTime = sw.Elapsed; 
} 

Répondre

12

J'ai eu ce même problème, ma requête prenait 40 secondes.

J'ai trouvé que le problème était avec les fonctions .Include("table_name"). Plus j'en avais, pire c'était.Au lieu de cela, j'ai changé mon code à Lazy Load toutes les données dont j'avais besoin juste après la requête, cela a fait tomber le temps total à environ 1,5 secondes de 40 secondes. Pour autant que je sache, cela accomplit exactement la même chose.

Donc, pour votre code, il serait quelque chose comme ceci:

var groupQuery = (from g in context.Groups 
      where g.GroupKey == 6 
      select g).OfType<Deal>(); 

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.... 
      } 
     } 
    } 
} 

Soit dit en passant, si vous deviez ajouter cette ligne de code ci-dessus la requête dans votre code actuel, il frappe le temps à environ 4 -5 secondes (encore trop lingue dans mon option) d'après ce que je comprends, l'option MergeOption.NoTracking désactive beaucoup de frais généraux de suivi pour la mise à jour et l'insertion des choses de nouveau dans la base de données:

context.groups.MergeOption = MergeOption.NoTracking; 
+3

Cela semble tellement contre-intuitif. Faire plusieurs requêtes SQL et charger le même nombre d'objets est plus rapide que de faire une requête SQL? – toxaq

+0

Chris vous semblez connaître beaucoup de choses sur le .include pouvez-vous s'il vous plaît jeter un oeil à mon poste http://stackoverflow.com/questions/10320174/speed-up-return-of-linq-entity-result –

+0

@bugz - J'ai regardé. Je suis désolé mec, je n'en ai aucune idée. Je devrais noter qu'à la fin j'étais extrêmement insatisfait avec ASP.net et cadre d'entité. Tout ce que j'ai fait a pris trop de temps. Je suis en train de réécrire cette application dans google app engine. –

4

C'est à cause de l'inclusion. Je suppose que vous êtes impatient de charger beaucoup d'objets dans la mémoire. Il faut beaucoup de temps pour construire les objets C# qui correspondent à vos entités db. Ma recommandation pour vous est d'essayer de charger paresseux seulement les données dont vous avez besoin.

+0

Loading Lazy wouldnt aider parce que nous avons tous besoin des objets inclus. Cela ne rapporte qu'environ 40 objets au total (1 Deal, 3 DealContracts, 3 Contracts, 3 contractitemdetails sur chaque contrat, et 1 de chacune des autres propriétés sur chaque contrat), donc je ne pense pas que ce serait trop intensif ...? – NotDan

+0

Je ne suis pas d'accord que le besoin de tous les objets signifie que le chargement paresseux n'aiderait pas. Voyez ma réponse pour la raison. Parfois, il est plus rapide d'exécuter une requête plus simple, puis de laisser les détails avec d'autres requêtes plus simples. –

0

EF prend un certain temps pour démarrer. Il a besoin de métadonnées de construction à partir de xml et génère probablement des objets 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, sauf de ne jamais redémarrer votre programme.

+2

Vraiment, il faut 30 secondes pour démarrer? Cela semble excessif. – billb

+0

Je ne l'ai pas vraiment utilisé avec des modèles compliqués, et il faut environ 5 secondes pour démarrer la plupart du temps. Vous objets semblent beaucoup plus complexes que ceux que j'ai. Essayez de faire une requête plus petite et voyez combien de temps cela prend. – AndreasN

+0

Ajout d'une requête simple (1 enregistrement, non inclus) avant que la requête compliquée sauve 2 secondes. Donc, il semble qu'il faut 2 secondes EF pour démarrer. Cela prend encore 28 secondes pour la première requête compliquée qui est trop longue. – NotDan

2

La seule façon de rendre la compilation initiale de la requête plus rapide que je connaisse est de rendre la requête moins complexe. La documentation MSDN sur performance considerations for the Entity Framework et Compiled Queries ne signifie pas qu'il n'y a aucune façon d'enregistrer une requête compilé pour une utilisation dans une session d'exécution de différentes applications.

J'ajouterais que nous avons trouvé que beaucoup d'inclusions peuvent rendre l'exécution de la requête plus lente que d'avoir moins d'inclusions et de faire plus de charges sur les entités apparentées plus tard. Quelques essais et erreurs sont nécessaires pour trouver le bon support.

Cependant, je dois vous demander si vous avez vraiment besoin de toutes les propriétés de chaque entité que vous incluez ici. Il me semble qu'il y a un grand nombre de types d'entités différents dans cette requête, donc les matérialiser pourrait bien être assez cher. Si vous essayez simplement d'obtenir des résultats tabulaires que vous n'avez pas l'intention de mettre à jour, en projetant le (relativement) petit nombre de champs dont vous avez vraiment besoin dans un appartement, le type anonyme devrait être significativement plus rapide pour diverses raisons. De plus, cela vous évite d'avoir à vous soucier du chargement, de charger Load/IsLoaded, etc.

Vous pouvez certainement accélérer la génération de vue initiale en précompilant les vues d'entité. Il y a documentation on MSDN for this. Mais puisque vous payez ce coût au moment où la première requête est exécutée, votre test avec une requête simple montre que cela fonctionne dans le voisinage de 2 secondes pour vous. C'est bien de dire que 2 secondes, mais ça ne sauvera rien d'autre.

+0

Malheureusement, j'ai vraiment besoin de toutes les propriétés de tous les objets. – NotDan

Questions connexes