儲存資料
雖然查詢可讓您從資料庫讀取資料,但儲存資料表示將新的實體新增至資料庫、移除實體或修改現有實體的屬性。 Entity Framework Core (EF Core) 支援兩種基本方法,將資料儲存至資料庫。
方法 1:變更追蹤和 SaveChanges
在許多情況下,您的程式必須查詢資料庫中的某些資料、對它執行一些修改,以及將這些修改儲存回去;這有時稱為「工作單位」。 例如,假設您有一組部落格,而且您想要變更 Url
其中一個部落格的 屬性。 在 EF 中,這通常如下所示:
using (var context = new BloggingContext())
{
var blog = context.Blogs.Single(b => b.Url == "http://example.com");
blog.Url = "http://example.com/blog";
context.SaveChanges();
}
上述程式碼會執行下列步驟:
- 它會使用一般 LINQ 查詢從資料庫載入實體(請參閱 查詢資料 )。 EF 的查詢預設會追蹤,這表示 EF 會追蹤其內部 變更追蹤器 中載入的實體。
- 載入的實體實例會如往常一樣操作,方法是指派 .NET 屬性。 EF 未參與此步驟。
- 最後, DbContext.SaveChanges() 呼叫 。 此時,EF 會自動藉由比較實體與載入實體的快照集,來偵測任何變更。 任何偵測到的變更會保存至資料庫;使用關係資料庫時,這通常牽涉到傳送,例如 SQL
UPDATE
來更新相關的資料列。
請注意,上述描述現有資料的一般更新作業,但新增和移除實體的類似原則會保留 。 您可以呼叫 DbSet<TEntity>.Add 和 Remove 來與 EF 的變更追蹤器互動,進而追蹤變更。 EF 接著會在呼叫 資料庫時 SaveChanges() 套用所有追蹤的變更(例如透過 SQL INSERT
和使用 DELETE
關係資料庫時)。
SaveChanges() 提供下列優點:
- 您不需要撰寫程式碼來追蹤哪些實體和屬性已變更 - EF 會自動為您執行這項作業,而且只會更新資料庫中的屬性,以改善效能。 想像一下,如果載入的實體系結至 UI 元件,允許使用者變更他們想要的任何屬性;EF 會承擔找出實際變更哪些實體和屬性的負擔。
- 儲存資料庫的變更有時可能很複雜! 例如,如果您想要新增部落格和該部落格的一些文章,您可能需要擷取插入部落格的資料庫產生金鑰,才能插入文章(因為它們需要參考部落格)。 EF 會為您執行這一切,並消除複雜性。
- EF 可以偵測並行問題,例如當查詢與 SaveChanges() 之間的其他人修改資料庫資料列時。 並行衝突 中 提供更多詳細資料。
- 在支援它的資料庫上, SaveChanges() 自動包裝交易中的多個變更,以確保您的資料在發生失敗時保持一致。 交易中 提供更多詳細資料。
- SaveChanges() 此外,在許多情況下也會分批多個變更,大幅減少資料庫往返次數,並大幅改善效能。 有效率的 更新提供 更多詳細資料。
如需基本 SaveChanges() 用法的詳細資訊和程式碼範例,請參閱 基本 SaveChanges 。 如需 EF 變更追蹤的詳細資訊,請參閱 變更追蹤概觀 。
方法 2:ExecuteUpdate 和 ExecuteDelete (「bulk update」)
注意
這項功能已在 EF Core 7.0 中引進。
雖然變更追蹤和 SaveChanges() 是儲存變更的強大方式,但它們確實有某些缺點。
首先, SaveChanges() 您必須查詢及追蹤您要修改或刪除的所有實體。 例如,如果您需要刪除評等低於特定閾值的所有部落格,您必須查詢、具體化和追蹤可能龐大的資料列,並 SaveChanges() 為每個資料列產生 DELETE
語句。 關係資料庫提供更有效率的替代方案:可以傳送單 DELETE
一命令,指定要透過 WHERE
子句刪除的資料列,但 SaveChanges() 模型不允許產生該資料列。
若要支援此「大量更新」案例,您可以使用 ExecuteDelete 如下:
context.Blogs.Where(b => b.Rating < 3).ExecuteDelete();
這可讓您透過一般 LINQ 運算子來表達 SQL DELETE
語句,這類似于一般 LINQ 查詢,導致對資料庫執行下列 SQL:
DELETE FROM [b]
FROM [Blogs] AS [b]
WHERE [b].[Rating] < 3
這會在資料庫中非常有效率地執行,而不需要從資料庫載入任何資料,或涉及 EF 的變更追蹤器。 同樣地, ExecuteUpdate 可讓您表達 SQL UPDATE
語句。
即使您未大量變更實體,也可能確切地知道您想要變更之實體的屬性。 使用變更追蹤 API 來執行變更可能過於複雜,需要建立實體實例、透過 Attach 追蹤它、進行變更,最後呼叫 SaveChanges() 。 針對這類案例, ExecuteUpdate
而且 ExecuteDelete
可以相當簡單的方式來表示相同的作業。
最後,變更追蹤和 SaveChanges() 本身都會造成特定的執行時間額外負荷。 如果您正在撰寫高效能應用程式, ExecuteUpdate
並 ExecuteDelete
可讓您避免這兩個元件,並有效率地產生您想要的語句。
不過,請注意 ExecuteUpdate
,和 ExecuteDelete
也有某些限制:
- 這些方法會立即執行,目前無法與其他作業批次處理。 另一方面, SaveChanges() 可以一起批次處理多個作業。
- 由於不涉及變更追蹤,因此您必須負責確切知道需要變更哪些實體和屬性。 這可能表示更手動、低階的程式碼追蹤需要變更的專案,以及哪些程式碼沒有變更。
- 此外,由於不涉及變更追蹤,因此在保存變更時,這些方法不會自動套用 並行控制 。 不過,您仍然可以明確新增
Where
子句來自行實作並行控制。 - 目前僅支援更新和刪除;插入必須透過 DbSet<TEntity>.Add 和 SaveChanges() 來完成。
如需詳細資訊和程式碼範例,請參閱 ExecuteUpdate
和 ExecuteDelete
。
摘要
以下是使用哪種方法的時機的一些指導方針。 請注意,這些不是絕對規則,而是提供實用的經驗法則:
- 如果您事先不知道要進行哪些變更,請使用
SaveChanges
;它會自動偵測需要套用哪些變更。 範例案例:- 「我想要從資料庫載入部落格,並顯示表單,讓使用者變更它」
- 如果您需要操作物件的圖形(也就是多個互連物件),請使用
SaveChanges
;它會找出變更的適當順序,以及如何將所有專案連結在一起。- 「我想更新部落格,變更其一些文章,並刪除其他文章」
- 如果您想要根據某些準則變更可能大量的實體,請使用
ExecuteUpdate
和ExecuteDelete
。 範例案例:- 「我想給所有員工加薪」
- 「我想刪除名稱開頭為 X 的所有部落格」
- 如果您已經確切知道您想要修改哪些實體,以及您想要如何變更實體,請使用
ExecuteUpdate
和ExecuteDelete
。 範例案例:- 「我想刪除名為 'Foo' 的部落格」
- 「我想將識別碼為 5 的部落格名稱變更為 'Bar'」