在 ADO.NET Entity Framework (EF) 的架構中,ObjectContext 扮演著核心角色,它是管理實體物件生命週期、追蹤變更、處理持久化以及協調資料庫交易的中心樞紐。本文將系統性地剖析 ObjectContext 的核心功能,涵蓋實體增刪改查、狀態管理、變更持久化、併發控制與交易處理等關鍵主題,並輔以實戰代碼範例,助您深入掌握 EF 開發的精髓。
新建的實體物件初始狀態為 EntityState.Detached(游離狀態),呼叫 AddObject 或強型別的 AddTo[EntitySet] 方法後,實體狀態轉為 EntityState.Added,標記為待插入資料庫。
using (var context = new MyContext())
{
var entity = new MyTab { ID = 10, Name = "wxwinter" };
Console.WriteLine(entity.EntityState); // Detached
context.AddToMyTab(entity); // 或 context.AddObject("MyTab", entity);
Console.WriteLine(entity.EntityState); // Added
context.SaveChanges();
}
DeleteObject 並不會從集合中移除實體,而是將其狀態標記為 EntityState.Deleted。下次呼叫 SaveChanges 時,EF 會發送 DELETE 命令。
var entity = context.MyTab.First(p => p.ID == 1);
context.DeleteObject(entity); // 狀態變為 Deleted
context.SaveChanges();
呼叫 Detach 可將實體從 ObjectContext 移除,狀態回歸 Detached。此後對實體的任何修改將不再被追蹤。
Modified。Modified。前提是 Context 中必須已存在該鍵值的實體。當實體經過序列化傳輸(如 WCF、Web Service)回到伺服器端,需重新附加至 Context 才能持久化變更。Attach 附加後狀態為 Unchanged,若需更新必須手動標記修改屬性(SetModifiedProperty),這比 ApplyPropertyChanges 更精確,可僅更新特定欄位。
var detachedEntity = new MyTab { ID = 1 };
detachedEntity.EntityKey = context.CreateEntityKey("MyTab", detachedEntity);
detachedEntity.Name = "Updated";
context.Attach(detachedEntity);
var ose = context.ObjectStateManager.GetObjectStateEntry(detachedEntity);
ose.SetModifiedProperty("Name"); // 僅標記 Name 為修改
context.SaveChanges();
EntityKey 封裝實體集名稱與鍵值,是實體在 Context 中的唯一身分證。可透過 CreateEntityKey 建立,再用 GetObjectByKey 或 TryGetObjectByKey 快速取得實體,避免重複查詢資料庫。
支援 Entity SQL (ESQL) 字串查詢,回傳 ObjectQuery<T>,適合動態組裝查詢條件的場景。
| 狀態 | 說明 |
|---|---|
| Detached | 實體不受任何 Context 管理 |
| Unchanged | 自上次 AcceptChanges 後未變更 |
| Added | 已加入 Context,待插入資料庫 |
| Deleted | 標記刪除,待從資料庫移除 |
| Modified | 屬性值已變更,待更新資料庫 |
透過 ObjectStateManager 可存取所有受管理實體的 ObjectStateEntry,提供以下關鍵方法:
GetObjectStateEntry(entity/EntityKey):取得單一實體的狀態條目TryGetObjectStateEntry(...):安全嘗試取得,避免拋出異常GetObjectStateEntries(EntityState):依狀態篩選實體集合(如取得所有 Added 實體)ObjectStateManagerChanged 事件:監聽實體加入、移除、重新整理等集合變更動作每個 ObjectStateEntry 暴露豐富的屬性與方法:
| 方法 | 行為 |
|---|---|
| SaveChanges() | 等同 SaveChanges(true):持久化後呼叫 AcceptAllChanges,所有狀態歸為 Unchanged |
| SaveChanges(true) | 持久化 + AcceptAllChanges |
| SaveChanges(false) | 僅持久化至資料庫,保留原有狀態(Added/Modified/Deleted 不變),適合需重試或分散式交易場景 |
在實際發送 SQL 指令前觸發,可在此攔截驗證、記錄審計日誌或修改實體值。
將 Context 中所有實體狀態強制設為 Unchanged,同步 OriginalValues。等同於對每個 ObjectStateEntry 呼叫 AcceptChanges()。
DefaultContainerName:指定預設實體容器名稱(對應 EDMX 中的 EntityContainer)Connection:底層 EntityConnection,可控制開啟/關閉、交易綁定CommandTimeout:SQL 命令逾時秒數MetadataWorkspace:存取 CSDL/SSDL/MSL 中繼資料,進階動態模型操作時使用在 EDM 設計器中將屬性的 ConcurrencyMode 設為 Fixed,EF 會在 UPDATE/DELETE 的 WHERE 子句中加入該欄位原始值比對。若資料庫值已被他人修改,拋出 OptimisticConcurrencyException。
不同 Context 實例擁有獨立快取。Context1 修改並存檔後,Context2 重新查詢才能看到最新資料,否則仍持有舊版本。
| 模式 | 行為 | 適用場景 |
|---|---|---|
| StoreWins (資料庫優先) | 以資料庫最新值覆寫本地修改,放棄使用者變更 | 管理員強制同步、唯讀資料重新整理 |
| ClientWins (客戶端優先) | 保留本地修改,並更新 OriginalValues 為資料庫最新值,後續 SaveChanges 不再報併發錯誤 | 使用者堅持提交變更、最後寫入者勝出 |
// 捕獲異常後處理
try { context.SaveChanges(); }
catch (OptimisticConcurrencyException)
{
context.Refresh(RefreshMode.ClientWins, entity); // 或 StoreWins
context.SaveChanges();
}
// 也可預先 Refresh 再 SaveChanges,避免異常
context.Refresh(RefreshMode.ClientWins, entity);
context.SaveChanges();
單次 SaveChanges 內的所有 INSERT/UPDATE/DELETE 自動包裹在一個資料庫交易中,任一失敗全盤回滾。
BeginTransaction/Commit/Rollback。注意:兩個 Context 共用同一連線交易易發生死結。using (var scope = new TransactionScope())
{
using (var ctx1 = new MyContext()) { ctx1.AddTo...(); ctx1.SaveChanges(); }
using (var ctx2 = new MyContext()) { ctx2.AddTo...(); ctx2.SaveChanges(); }
scope.Complete(); // 全部成功才提交
}
ObjectContext 是 Entity Framework 3.5/4.0 時代的核心類別(EF Core 已改為 DbContext),掌握其狀態管理、變更追蹤、併發與交易機制,是構建健壯、高效能資料存取層的關鍵。希望本文詳細的分類說明與代碼範例,能成為您 EF 開發路上的得力參考。