Effektiv uppdatering
Dosering
EF Core hjälper till att minimera tur och retur genom att automatiskt batcha ihop alla uppdateringar på en enda tur och retur. Tänk på följande:
var blog = await context.Blogs.SingleAsync(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" });
await context.SaveChangesAsync();
Ovanstående läser in en blogg från databasen, ändrar dess URL och lägger sedan till två nya bloggar. för att tillämpa detta skickas två SQL INSERT-instruktioner och en UPDATE-instruktion till databasen. I stället för att skicka dem en i taget, när blogginstanser läggs till, spårar EF Core dessa ändringar internt och kör dem på en enda tur och retur när SaveChanges anropas.
Antalet instruktioner som EF batchar i en enda runda beror på vilken databasleverantör som används. Prestandaanalys har till exempel visat att batchbearbetning i allmänhet är mindre effektivt för SQL Server när färre än 4 instruktioner ingår. På samma sätt försämras fördelarna med batchbearbetning efter cirka 40 -instruktioner för SQL Server, så EF Core kör som standard endast upp till 42 -instruktioner i en enda batch och kör ytterligare instruktioner i separata tur och retur.
Användare kan också justera dessa tröskelvärden för att uppnå potentiellt högre prestanda – men jämför noggrant innan de ändrar dessa:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(
@"Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True",
o => o
.MinBatchSize(1)
.MaxBatchSize(100));
}
Använd ExecuteUpdate och ExecuteDelete när det är relevant
Anta att du vill ge alla dina anställda en höjning. En typisk implementering för detta i EF Core skulle se ut så här:
foreach (var employee in context.Employees)
{
employee.Salary += 1000;
}
await context.SaveChangesAsync();
Även om det här är en helt giltig kod ska vi analysera vad den gör ur ett prestandaperspektiv:
- En databas tur och retur utförs för att läsa in alla relevanta anställda. Observera att detta ger alla anställdas raddata till klienten, även om endast lönen behövs.
- EF Cores ändringsspårning skapar ögonblicksbilder när entiteterna läses in och jämför sedan ögonblicksbilderna med instanserna för att ta reda på vilka egenskaper som har ändrats.
- Vanligtvis utförs ett andra databasanrop för att spara alla ändringar (observera att vissa databasleverantörer delar upp ändringarna i flera anrop). Även om det här batchbearbetningsbeteendet är mycket bättre än att göra en tur och retur för varje uppdatering, skickar EF Core fortfarande en UPDATE-instruktion per anställd, och databasen måste köra varje instruktion separat.
Från och med EF Core 7.0 kan du använda metoderna ExecuteUpdateAsync
och ExecuteDeleteAsync
för att göra samma sak mycket mer effektivt:
await context.Employees.ExecuteUpdateAsync(s => s.SetProperty(e => e.Salary, e => e.Salary + 1000));
Detta skickar följande SQL-instruktion till databasen:
UPDATE [Employees] SET [Salary] = [Salary] + 1000;
Den här UPDATE
utför hela åtgärden på en enda tur och retur, utan att läsa in eller skicka några faktiska data till databasen, och utan att använda EF:s ändringsspårningsmaskiner, vilket medför ytterligare omkostnader. Mer information finns i ExecuteUpdate
och ExecuteDelete
.
Om du använder en äldre version av EF Core som ännu inte stöder ExecuteUpdate
och ExecuteDelete
, eller vill köra en komplex SQL-instruktion som inte stöds av dessa metoder, kan du fortfarande använda en SQL-fråga för att utföra åtgärden:
context.Database.ExecuteSql($"UPDATE [Employees] SET [Salary] = [Salary] + 1000");
Mer information om skillnaderna mellan SaveChanges
och ExecuteUpdate
/ExecuteDelete
finns på sidan Översikt om hur du sparar data.