Effizientes Aktualisieren

Batchverarbeitung

EF Core trägt dazu bei, Roundtrips zu minimieren, indem alle Updates automatisch in einem einzelnen Roundtrip als Batch zusammengefasst werden. Beachten Sie Folgendes:

var blog = context.Blogs.Single(b => b.Url == "http://someblog.microsoft.com");
blog.Url = "http://someotherblog.microsoft.com";
context.Add(new Blog { Url = "http://newblog1.microsoft.com" });
context.Add(new Blog { Url = "http://newblog2.microsoft.com" });
context.SaveChanges();

Das Obige lädt einen Blog aus der Datenbank, ändert seine URL und fügt dann zwei neue Blogs hinzu. Um dies anzuwenden, werden zwei SQL INSERT-Anweisungen und eine UPDATE-Anweisung an die Datenbank gesendet. Anstatt sie nacheinander zu senden, wenn Blog-Instanzen hinzugefügt werden, verfolgt EF Core diese Änderungen intern und führt sie in einem einzigen Roundtrip aus, wenn SaveChanges aufgerufen wird.

Die Anzahl der Anweisungen, die EF in einem einzelnen Roundtrip als Batch zusammenfasst, hängt vom verwendeten Datenbankanbieter ab. Die Leistungsanalyse hat beispielsweise gezeigt, dass die Batchverarbeitung für SQL Server in der Regel weniger effizient ist, wenn weniger als 4 Anweisungen beteiligt sind. Ebenso lässt der Nutzen der Batchverarbeitung bei SQL Server nach etwa 40 Anweisungen nach, so dass EF Core standardmäßig nur bis zu 42 Anweisungen in einem einzigen Batch ausführt und weitere Anweisungen in separaten Roundtrips ausführt.

Benutzer können diese Schwellenwerte auch anpassen, um eine potenziell höhere Leistung zu erzielen. Führen Sie jedoch sorgfältige Benchmarktests durch, bevor Sie diese ändern:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder.UseSqlServer(
        @"Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True",
        o => o
            .MinBatchSize(1)
            .MaxBatchSize(100));
}

Verwenden Sie ExecuteUpdate und ExecuteDelete, wenn relevant

Nehmen wir an, Sie möchten allen Ihren Mitarbeitern eine Lohnerhöhung geben. In EF Core würde eine typische Implementierung für dies wie folgt aussehen:

foreach (var employee in context.Employees)
{
    employee.Salary += 1000;
}
context.SaveChanges();

Dies ist zwar ein absolut gültiger Code, aber lassen Sie uns analysieren, was er unter dem Gesichtspunkt der Leistung bewirkt:

  • Es wird ein Datenbankroundtrip durchgeführt, um alle relevanten Mitarbeiter zu laden. Beachten Sie, dass dadurch alle Zeilendaten der Mitarbeiter zum Kunden gelangen, auch wenn nur das Gehalt benötigt wird.
  • Die Änderungsnachverfolgung von EF Core erstellt Momentaufnahmen beim Laden der Entitäten und vergleicht dann diese Momentaufnahmen mit den Instanzen, um herauszufinden, welche Eigenschaften geändert wurden.
  • In der Regel wird ein zweiter Datenbankroundtrip ausgeführt, um alle Änderungen zu speichern (beachten Sie, dass einige Datenbankanbieter die Änderungen in mehrere Roundtrips aufteilen). Obwohl dieses Batchverarbeitungsverhalten wesentlich besser ist als ein Roundtrip für jede Aktualisierung, sendet EF Core weiterhin eine UPDATE-Anweisung pro Mitarbeiter, und die Datenbank muss jede Anweisung separat ausführen.

Ab EF Core 7.0 können Sie die ExecuteUpdate- und ExecuteDelete-Methoden verwenden, um dasselbe wesentlich effizienter zu erledigen:

context.Employees.ExecuteUpdate(s => s.SetProperty(e => e.Salary, e => e.Salary + 1000));

Dies sendet die folgende SQL-Anweisung an die Datenbank:

UPDATE [Employees] SET [Salary] = [Salary] + 1000;

Dieser UPDATE führt den gesamten Vorgang in einem einzelnen Roundtrip aus, ohne tatsächliche Daten in die Datenbank zu laden oder zu ihr zu senden, und ohne die EF-Maschinerie zur Änderungsnachverfolgung zu nutzen, die einen zusätzlichen Aufwand verursacht. Weitere Informationen finden Sie unter ExecuteUpdate und ExecuteDelete.

Wenn Sie eine ältere Version von EF Core verwenden, die ExecuteUpdate und ExecuteDelete noch nicht unterstützt, oder wenn Sie eine komplexe SQL-Anweisung ausführen möchten, die von diesen Methoden nicht unterstützt wird, können Sie weiterhin eine SQL-Abfrage verwenden, um den Vorgang auszuführen:

context.Database.ExecuteSql($"UPDATE [Employees] SET [Salary] = [Salary] + 1000");

Weitere Informationen zu den Unterschieden zwischen SaveChanges und ExecuteUpdate/ExecuteDelete finden Sie auf der Übersichtsseite zum Speichern von Daten.