2017-07-24 2 views
0

Wir haben eine Web MVC .NET-Anwendung, die den ersten Codeansatz verwendet. Bei der Durchführung grundlegender CRUD-Vorgänge verhält sich alles normal, aber unsere Berichte und Aufgaben mit langer Laufzeit dauern ewig..Net C# -Anwendung - Korrekte Methode zur Lösung von Leistungsproblemen im Zusammenhang mit Änderungsnachverfolgung im Entitätsframework

Die meisten unserer problematischen Fällen kann durch die folgenden zwei Szenarien wieder aufgenommen werden:

  1. Es ist eine Aufgabe, die iteriert auf 10.000+ Artikel (mit sqlDataReader), ein paar Änderungen vornimmt und erstellt eine Kopie für jeden von ihnen.
  2. Wir haben Berichte, die auf 100.000 Elemente (unter Verwendung sqlDataReader) iteriert, um einige Informationen zu lesen und in eine Datei zu schreiben. in Entity Framework Änderungsverfolgung:

Nach der Realisierung, dass die Zeit, um unsere Aufgaben zu verarbeiten erforderlich und Berichte wuchsen exponentiell (O(n^2)) mit der Anzahl der Elemente, die Wurzel des Problems zu verarbeiten, untersuchten wir weiter und fanden heraus.

Alles funktioniert gut, wenn wir AutoDetectChangesEnabled deaktivieren, aber jeder Artikel, den wir zu diesem Thema gelesen haben, sagt, wir sollten dies vermeiden, wann immer es möglich ist.

Also hier ist meine Frage: Deaktiviert AutoDetectChangesEnabled die einzige oder die empfohlene Art der Lösung dieser Art von Problemen? Gibt es eine andere saubere Lösung? Wie würdest du das angehen?

Das setzt voraus, wir all Empfehlungen in dieser Serie von Artikeln erwähnt würden folgen: https://blog.oneunicorn.com/2012/03/10/secrets-of-detectchanges-part-1-what-does-detectchanges-do/

Hier ein kleines Codebeispiel 2 darstellt Szenario:

//SQL query to fetch 100,000+ items 
SqlCommand command = new SqlCommand(query, conn); 

//Use SqlDataReader to stream results 
using (SqlDataReader reader = command.ExecuteReader()) 
{ 
    /*In the following loop, if we use SqlCommand instead of LINQ, it's fast and steady. 
     But as soon as we start using LINQ, each iteration takes exponentially more time 
     to complete - even if we are only making read operations. Unless we disable 
     AutoDetectChangesEnabled: then it's fast and steady.*/ 
    while (reader.Read()) 
    { 
     /*Fetch additional information. I know this could be done with a join in the 
      original query but we have a lot of additional info to fetch and the query would 
      be too heavy (this is a simplified version of the code).*/ 
     Item item = db.Items.Where(x => x.OtherItem_Id == (int)reader["Id"]).FirstOrDefault(); 

     //Read properties 
     prop1 = item.prop1; 
     prop2 = item.prop2; 
     prop3 = item.prop3; 

     //Use those properties to add an entry to a csv file 
    } 
} 

Dank!

+0

Sie rufen hier eine sehr breite Frage zu stellen, gibt es keine eine Möglichkeit, zu debuggen oder das Performance-Problem lösen Sie sind Performance-Probleme sind immer sehr schwierig zu beheben und immer eine Vielzahl von möglichen Lösungen –

+0

Sie können nur sagen, weg von AutoDetectChanges zu deaktivieren, da ich denke, dass EF könnte nicht wissen, welche Datensätze sind schmutzig auf SaveChanges, obwohl ich sein könnte falsch, nur eine Vermutung, habe nicht mit EF in einer Weile gearbeitet –

+0

Es kann richtig gemacht werden (zB mit der Eigenschaft API, um Felder zu aktualisieren), aber ich stimme zu, es ist nicht etwas, das sollte wenn möglich deaktiviert werden. Deshalb frage ich die Meinung der Community hier. Ich werde ein Beispiel veröffentlichen, damit Sie eine bessere Idee haben können. – Alexandre

Antwort

1

Es ist zur Zeit schwierig, Ihre Frage ohne ein konkretes Beispiel zu beantworten. Wie Brian im Kommentarabschnitt sagte, können sie eine Vielzahl von möglichen Lösungen haben ...

Zum Beispiel könnte eine Lösung so einfach sein wie die Verwendung von AddRange, anstatt Entity jedes Mal hinzuzufügen, wenn Sie Ihren DataReader durchlaufen.

Die DetectChanges-Methode wird nur einmal statt X-mal aufgerufen.


Haftungsausschluss: Ich bin der Besitzer des Projekts Entity Framework Extensions

Eine andere Lösung das gleiche Problem, das ich vor kurzem erhielt sein könnte. Das Erkennen von Änderungen dauerte über 1 Stunde für jemanden, der ein sehr komplexes und großes Modell hatte.

Anstatt SaveChanges zu verwenden, haben wir ihm einfach vorgeschlagen, ihn mit BulkInsert direkt aus unserer Bibliothek einzufügen: Entity Framework Extensions, was die Leistung um mehr als das 50fache verbessert hat.


EDIT: Antwort Unterfrage

wenn AutoDetectChanges aktiviert sind, wird es Dinge nach unten, auch für den Lesebetrieb

Einige Leseverfahren wie finden automatisch die DetectChanges nennen verlangsamt, Ja, einige Leseoperationen werden auch von den DetectChanges

beeinflusst
+0

Merkwürdige Sache ist: Wenn AutoDetectChanges aktiviert sind, verlangsamt es Dinge sogar für Lesevorgänge (wir ändern keine Werte und rufen keine Methode auf, die DetectChanges verwendet). Ich werde versuchen, ein Beispiel des Codes einzufügen. – Alexandre

1

Es gibt möglicherweise mehrere Möglichkeiten, wie Sie die Leistung verbessern können, aber wie bereits erwähnt, ist es schwierig, diese Art von Problemen zu beheben, da andere Teile des Codes Auswirkungen auf dieses Problem haben können. Mit diesem gesagt, hier sind ein paar Ideen.

1 - Ihre EF LINQ-Abfrage befindet sich in einer selbst beschriebenen Schleife von 100.000 Elementen. Das bedeutet, dass Sie während dieser Schleife 100.000 Abfragen an Ihre Datenbank ausgeben. Abhängig von der Tabellengröße und -komplexität Items können Sie alle diese Datensätze in den Speicher abrufen und dann Ihre LINQ-Befehle in einer speicherinternen Liste ausführen. Dies würde Ihre SQL-Abfragen um 99.999 reduzieren.

using (SqlDataReader reader = command.ExecuteReader()) 
{ 
    //pull the entire table to an in-memory list. 
    var items = db.Items.ToList(); 
    while (reader.Read()) 
    { 
     Item item = items.Where(x => x.OtherItem_Id == (int)reader["Id"]).FirstOrDefault(); 
     prop1 = item.prop1; 
     prop2 = item.prop2; 
     prop3 = item.prop3; 
    } 
} 

2 - Sie können die NoTracking direkt in Ihrer Abfrage auslösen. Wenn dies wirklich nur lesbar Datensatz, dann würden Sie nicht in Gefahr sein, da Sie nicht db.SaveChanges()

using (SqlDataReader reader = command.ExecuteReader()) 
{ 
    while (reader.Read()) 
    { 
     Item item = db.Items.AsNoTracking().Where(x => x.OtherItem_Id == (int)reader["Id"]).FirstOrDefault(); 

     prop1 = item.prop1; 
     prop2 = item.prop2; 
     prop3 = item.prop3; 
    } 
} 
Verwandte Themen