2009-03-26 4 views
16

Die executeTime unten ist 30 Sekunden das erste Mal und 25 Sekunden das nächste Mal, wenn ich den gleichen Code ausführen. Wenn ich in SQL Profiler schaue, sehe ich sofort eine Anmeldung, dann sitzt sie für ungefähr 30 Sekunden dort. Sobald die select-Anweisung ausgeführt wird, beendet die App den ToList-Befehl. Wenn ich die generierte Abfrage von Management Studio aus führe, dauert die Datenbankabfrage nur etwa 400 ms. Es gibt 14 Zeilen und 350 Spalten zurück. Es sieht so aus, als ob die Umwandlung der Datenbankergebnisse in die Entitäten so kurz ist, dass sie nicht bemerkbar ist.Warum benötigt Entity Framework 30 Sekunden zum Laden von Datensätzen, wenn die generierte Abfrage nur eine halbe Sekunde dauert?

Was passiert also in den 30 Sekunden vor dem Datenbankanruf?

Wenn das Entity-Framework so langsam ist, können wir es nicht verwenden. Gibt es etwas, das ich falsch mache oder etwas, das ich ändern kann, um das dramatisch zu beschleunigen?

UPDATE: In Ordnung, wenn ich eine kompilierte Abfrage verwenden, ist es das erste Mal 30 Sekunden dauern, und das zweite Mal, dauert es 1/4-Sekunden. Kann ich etwas tun, um den ersten Anruf zu beschleunigen?

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

Antwort

12

Ich hatte genau dieses Problem, meine Abfrage dauerte 40 Sekunden.

Ich fand das Problem war mit den Funktionen. Je mehr ich hatte, desto schlimmer war es.Stattdessen änderte ich meinen Code in Lazy Load alle Daten, die ich direkt nach der Abfrage brauchte, was die Gesamtdauer von 40 Sekunden auf etwa 1,5 Sekunden reduzierte. Soweit ich weiß, erfüllt dies genau das Gleiche.

Also für Ihren Code würde es so etwas wie diese:

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

übrigens, wenn Sie diese Codezeile über der Abfrage in Ihrem aktuellen Code hinzufügen, würde es die Zeit bis hinab zu etwa klopft 4 -5 Sekunden (noch zu ling in meiner Option) von dem, was ich verstehe, die MergeOption.NoTracking Option eine Menge des Tracking-Overhead deaktiviert für die Aktualisierung und zurück in die Datenbank Sachen Einfügen:

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

Dies scheint so kontra intuitiv. Das Erstellen mehrerer SQL-Anfragen und das Laden der gleichen Anzahl von Objekten ist schneller als das Erstellen einer SQL-Anfrage? – toxaq

+0

Chris Sie scheinen eine Menge über die .include wissen, können Sie bitte einen Blick auf meine Post http://stackoverflow.com/questions/10320174/speed-up-return-of-linq-entity-result –

+0

@bugz - Ich habe nachgesehen. Es tut mir leid, Mann, ich habe keine Ahnung. Ich sollte zur Kenntnis nehmen, dass ich am Ende mit ASP.net und dem Entity Framework extrem unzufrieden war. Alles, was ich gemacht habe, hat viel zu lange gedauert. Ich schreibe gerade diese ganze Anwendung in Google App Engine um. –

4

Es ist wegen der Include. Meine Vermutung ist, dass Sie eifrig sind, viele Objekte in den Speicher zu laden. Es dauert viel Zeit, um die C# -Objekte zu erstellen, die Ihren DB-Entitäten entsprechen.

Meine Empfehlung für Sie ist zu versuchen, nur die Daten lazy zu laden, die Sie brauchen.

+0

Lazy Loading-würde nicht helfen, weil wir alle brauchen der enthaltenen Objekte. Es gibt wirklich nur etwa 40 Objekte insgesamt (1 Deal, 3 DealContracts, 3 Contracts, 3 Vertragsdetails für jeden Vertrag und 1 von jedem der anderen Eigenschaften für jeden Vertrag), also würde ich nicht denken, dass es zu intensiv wäre ...? – NotDan

+0

Ich bin nicht damit einverstanden, dass die Notwendigkeit aller Objekte bedeutet, dass das Lazy Loading nicht helfen würde. Siehe meine Antwort für den Grund. Manchmal ist es schneller, eine einfachere Abfrage auszuführen und dann die Details mit anderen einfacheren Abfragen zu verknüpfen. –

0

EF benötigt etwas Zeit zum Starten. Es benötigt Build-Metadaten von XML und generiert wahrscheinlich Objekte, die für das Mapping verwendet werden. Es dauert also ein paar Sekunden, um zu starten, ich glaube nicht, dass es einen Weg gibt, um das zu umgehen, außer das Programm nie neu zu starten.

+2

Wirklich, es braucht 30 Sekunden zum Starten? Das scheint übertrieben. – billb

+0

Ich habe es nicht wirklich mit komplizierten Modellen verwendet, und es dauert etwa 5 Sekunden, um die meiste Zeit zu starten. Ihre Objekte sehen viel komplexer aus als die, die ich habe. Versuchen Sie eine kleinere Abfrage durchzuführen und sehen Sie, wie lange das dauert. – AndreasN

+0

Hinzufügen einer einfachen Abfrage (1 Datensatz, keine Includes) vor der komplizierten Abfrage spart 2 Sekunden. Es scheint also, dass EF 2 Sekunden benötigt, um zu starten. Es dauert immer noch 28 Sekunden für die erste komplizierte Abfrage, die zu lang ist. – NotDan

2

Die einzige Möglichkeit, die erste Kompilierung der Abfrage schneller zu machen, die ich kenne, besteht darin, die Abfrage weniger komplex zu machen. Die MSDN-Dokumentation auf performance considerations for the Entity Framework und Compiled Queries zeigen nicht an, dass es eine Möglichkeit gibt, eine kompilierte Abfrage für Verwendung in einer anderen Anwendungsausführungssitzung zu speichern.

Ich würde hinzufügen, dass wir festgestellt haben, dass eine Menge von Includes die Abfrageausführung verlangsamen kann, als dass weniger Includes und mehr Loads für verwandte Entitäten später ausgeführt werden. Um das richtige Medium zu finden, bedarf es einiger Versuche.

Allerdings muss ich fragen, ob Sie wirklich jede Eigenschaft von jeder Entität benötigen, die Sie hier einschließen. Es scheint mir, dass es in dieser Abfrage eine große Anzahl verschiedener Entitätstypen gibt, so dass deren Materialisierung ziemlich teuer sein könnte. Wenn Sie nur tabellarische Ergebnisse erzielen möchten, die Sie nicht aktualisieren möchten, sollten Sie die (relativ) weniger Felder, die Sie tatsächlich benötigen, in einen flachen, anonymen Typ umwandeln, der aus verschiedenen Gründen deutlich schneller ist. Außerdem müssen Sie sich nicht mehr um das eifrige Laden kümmern und müssen Load/IsLoaded usw. aufrufen.

Sie können die Generierung der ursprünglichen Ansicht beschleunigen, indem Sie die Entity-Ansichten vorkompilieren. Es gibt documentation on MSDN for this. Aber da Sie diese Kosten zum Zeitpunkt der ersten Abfrage bezahlen, zeigt Ihr Test mit einer einfachen Abfrage, dass diese in der Nähe von 2 Sekunden für Sie läuft. Es ist nett zu sagen, dass 2 Sekunden, aber es wird nichts anderes speichern.

+0

Leider brauche ich wirklich alle Eigenschaften für alle Objekte. – NotDan

Verwandte Themen