Warum ist das Einfügen von Entitäten in EF 4.1 im Vergleich zu ObjectContext so langsam?

81

Grundsätzlich füge ich 35000 Objekte in eine Transaktion ein:

using(var uow = new MyContext()){
  for(int i = 1; i < 35000; i++) {
     var o = new MyObject()...;
     uow.MySet.Add(o);
  }
  uow.SaveChanges();
}

Das dauert ewig! Wenn ich das zugrunde liegende ObjectContext verwende (indem ich es verwende IObjectAdapter), ist es immer noch langsam, dauert aber ungefähr 20 Sekunden. Es sieht so aus, DbSet<>als würde man einige lineare Suchen durchführen, was sehr viel Zeit in Anspruch nimmt ...

Hat noch jemand dieses Problem gesehen?

Hartmut
quelle
3
Ich glaube irgendwie, dass die Antwort ähnlich sein wird: stackoverflow.com/questions/5917478/…
Ladislav Mrnka

Antworten:

127

Wie bereits von Ladislav im Kommentar angegeben, müssen Sie die automatische Änderungserkennung deaktivieren, um die Leistung zu verbessern:

context.Configuration.AutoDetectChangesEnabled = false;

Diese Änderungserkennung ist in der DbContextAPI standardmäßig aktiviert .

Der Grund, warum DbContextsich das ObjectContextVerhalten von der API so unterscheidet, ist, dass viel mehr Funktionen der DbContextAPI DetectChangesintern aufgerufen werden als Funktionen der ObjectContextAPI, wenn die automatische Änderungserkennung aktiviert ist.

Hier finden Sie eine Liste der Funktionen, die DetectChangesstandardmäßig aufgerufen werden. Sie sind:

  • Die Add, Attach, Find, Local, oder RemoveMitglieder aufDbSet
  • Die GetValidationErrors, Entryoder SaveChangesMitglieder aufDbContext
  • Die EntriesMethode aufDbChangeTracker

Besonders AddAnrufe, DetectChangesdie für die schlechte Leistung verantwortlich sind, die Sie erlebt haben.

Im Gegensatz dazu ObjectContextruft die API DetectChangesnur automatisch in, SaveChangesaber nicht in AddObjectund den anderen oben genannten entsprechenden Methoden auf. Dies ist der Grund, warum die Standardleistung von ObjectContextschneller ist.

Warum haben sie diese standardmäßige automatische Änderungserkennung DbContextin so vielen Funktionen eingeführt? Ich bin mir nicht sicher, aber es scheint, dass das Deaktivieren und DetectChangesmanuelle Aufrufen an den richtigen Stellen als fortgeschritten angesehen wird und leicht subtile Fehler in Ihre Anwendung einbringen kann. Verwenden Sie [es] daher mit Vorsicht .

Slauma
quelle
@Ladislav: Sie haben Recht, ich habe dies nicht gefunden, da ich nur nach Einfügeproblemen gesucht habe :-(
Hartmut
Danke für die Erklärung. Ich habe tatsächlich context.Configuration.AutoDetectChangesEnabled = false aufgerufen, aber ich habe es während der Datenbankerstellung in der Seed () -Methode getan. Ich dachte, dies würde den Standard festlegen. Mir war nicht bewusst, dass ich es für jede Instanz aufrufen muss. Vielen Dank!
Hartmut
3
@Hartmut: Sie können die Änderungserkennung im Konstruktor Ihres abgeleiteten DbContext deaktivieren, dann haben Sie sie immer deaktiviert. Aber persönlich macht mich diese Bemerkung über "möglicherweise subtile Fehler einführen", wenn sie deaktiviert ist, nervös. Ich habe die Änderungserkennung standardmäßig aktiviert und deaktiviere sie nur in Codeblöcken wie Ihrem, in denen die Leistungssteigerung offensichtlich ist und in denen ich mich sicher fühle, dass sie keine Probleme verursacht.
Slauma
Ich stimme zu, ich habe nur einen leistungskritischen Teil meiner Anwendung getestet. Im Produktionscode ist es am besten, ihn auf Fälle wie Masseneinsätze usw. zu beschränken
Hartmut
Vielen Dank für diese Antwort. Viele Informationen da draußen, aber das kommt auf den Punkt!
Fred Wilson
11

Kleiner empirischer Test mit EF 4.3 CodeFirst:

1000 Objekte mit AutoDetectChanges = true entfernt: 23 Sek

1000 Objekte mit AutoDetectChanges = false entfernt: 11 Sek

1000 Objekte mit AutoDetectChanges = true eingefügt: 21 Sek

1000 Objekte mit AutoDetectChanges = false eingefügt: 13 Sek

Zax
quelle
1
Danke Zax. Was sind Ihre Ergebnisse mit 35.000 gemäß der Frage? Sie werden sehen, dass in der ursprünglichen Frage angegeben ist, dass die Leistung quadratisch abfällt
Daniel Dyson
8

In .netcore 2.0 wurde dies verschoben zu:

context.ChangeTracker.AutoDetectChangesEnabled = false;

Maxvt
quelle
1

Neben den Antworten haben Sie hier gefunden. Es ist wichtig zu wissen, dass auf Datenbankebene mehr Arbeit zum Einfügen als zum Hinzufügen erforderlich ist. Die Datenbank muss neuen Speicherplatz erweitern / zuweisen. Dann muss mindestens der Primärschlüsselindex aktualisiert werden. Obwohl Indizes beim Aktualisieren möglicherweise auch aktualisiert werden, ist dies weitaus seltener. Wenn Fremdschlüssel vorhanden sind, müssen diese Indizes ebenfalls gelesen werden, um sicherzustellen, dass die referenzielle Integrität erhalten bleibt. Trigger können ebenfalls eine Rolle spielen, obwohl diese Updates auf die gleiche Weise beeinflussen können.

All diese Datenbankarbeit ist bei täglichen Einfügetätigkeiten sinnvoll, die durch Benutzereingaben entstehen. Aber wenn Sie nur eine vorhandene Datenbank hochladen oder einen Prozess haben, der viele Einfügungen generiert. Vielleicht möchten Sie nach Möglichkeiten suchen, dies zu beschleunigen, indem Sie es auf das Ende verschieben. Normalerweise ist das Deaktivieren von Indizes beim Einfügen üblich. Es gibt sehr komplexe Optimierungen, die je nach Fall durchgeführt werden können, sie können etwas überwältigend sein.

Beachten Sie nur, dass das Einfügen im Allgemeinen länger dauert als Aktualisierungen.

Arturo Hernandez
quelle