有效率的更新
分批處理
EF Core 可透過在單一往返中自動批處理所有更新,以協助將往返時間降到最低。 請考慮下列事項:
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();
上述會從資料庫載入部落格、變更其URL,然後新增兩個新的部落格:若要套用這項功能,則會將兩個 SQL INSERT 語句和一個 UPDATE 語句傳送至資料庫。 EF Core 會在內部追蹤這些變更,而不是逐一傳送這些變更,當 Blog 實例被加入時,並會在呼叫 SaveChanges 時以單一往返方式執行這些變更。
EF 批次在一次往返的執行中所包含的語句數,取決於所使用的資料庫提供者。 例如,當涉及少於 4 個語句時,效能分析已顯示批處理對 SQL Server 而言通常效率較低。 同樣地,在 SQL Server 大約 40 個語句之後,批處理的優點會降低,因此 EF Core 預設只會在單一批次中執行最多 42 個語句,並在後續的多次往返中執行其他語句。
使用者也可以調整這些臨界值,以達到可能更高的效能 -- 但在修改這些值之前,請謹慎基準檢驗:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(
@"Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True",
o => o
.MinBatchSize(1)
.MaxBatchSize(100));
}
相關時使用ExecuteUpdate和ExecuteDelete
假設您想要向所有員工提供加薪。 EF Core 中這個的一般實作如下所示:
foreach (var employee in context.Employees)
{
employee.Salary += 1000;
}
await context.SaveChangesAsync();
雖然這是完全有效的程序代碼,但讓我們從效能的觀點來分析其用途:
- 執行資料庫查詢往返以載入所有相關員工資料;請注意,這會將所有員工的行數據導入到用戶端,即使僅需要薪資數據也會這樣做。
- EF Core 的變更追蹤會在載入實體時建立快照集,然後將這些快照集與實例進行比較,以找出哪些屬性已變更。
- 一般而言,會執行第二次資料庫往返以儲存所有變更(請注意,有些資料庫提供者會將變更分割成多次往返)。 雖然此批處理行為遠勝於每次更新往返,但 EF Core 仍會為每個員工傳送 UPDATE 語句,而且資料庫必須個別執行每個語句。
從 EF Core 7.0 開始,您可以使用 ExecuteUpdateAsync
和 ExecuteDeleteAsync
方法來更有效率地執行相同的動作:
await context.Employees.ExecuteUpdateAsync(s => s.SetProperty(e => e.Salary, e => e.Salary + 1000));
這會將下列 SQL 語句傳送至資料庫:
UPDATE [Employees] SET [Salary] = [Salary] + 1000;
此 UPDATE
會以單一往返方式執行整個作業,而不需要載入或傳送任何實際數據至資料庫,而且不使用EF的變更追蹤機制,這會產生額外的額外負荷。 如需詳細資訊,請參閱 ExecuteUpdate
和 ExecuteDelete
。
如果您使用尚未支援 ExecuteUpdate
和 ExecuteDelete
的舊版 EF Core,或想要執行這些方法不支援的複雜 SQL 語句,您仍然可以使用 SQL 查詢來執行作業:
context.Database.ExecuteSql($"UPDATE [Employees] SET [Salary] = [Salary] + 1000");
若要深入瞭解 SaveChanges
與 ExecuteUpdate
/ExecuteDelete
之間的差異,請參閱儲存數據的 概觀頁面。