首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

ADO.NET Entity Framework 深度解析(七):變更追蹤與 ObjectStateManager 實戰

     編輯:關於.NET

在 Entity Framework (EF) 的核心架構中,ObjectStateManager 扮演著「變更追蹤中樞」的角色。它隸屬於 ObjectContext,負責監控所有受管理實體物件的狀態變化(新增、修改、刪除),並在呼叫 SaveChanges() 時,依據追蹤資訊自動生成對應的 T-SQL 指令批次送往資料庫執行。相較於 LINQ to SQL 的 DataContext,EF 的變更追蹤機制更為精細且功能豐富。

ObjectStateManager 核心方法

開發者常用的兩個關鍵方法:

  • GetObjectStateEntries(EntityState state):依指定狀態(Added、Modified、Deleted、Unchanged 等)回傳 ObjectStateEntry 集合。
  • GetObjectStateEntry(object entity):取得特定實體對應的 ObjectStateEntry。

每個 ObjectStateEntry 封裝了豐富的變更資訊:

  • EntityKey:實體的唯一識別鍵值。
  • OriginalValues:實體載入時的原始屬性值(資料庫當前值)。
  • CurrentValues:實體目前在記憶體中的屬性值(含使用者修改)。
  • State:目前狀態枚舉值。

實戰範例:檢視修改前後的欄位值

以下程式碼示範如何擷取狀態為 Modified 的實體,並逐一列印其主鍵、原始值與目前值:

using (var context = new AdventureWorksLTEntities())
{
    // 1. 擷取實體並修改屬性
    var product = context.Product.FirstOrDefault(p => p.ProductID == 1004);
    if (product != null)
    {
        product.Color = "Black";
        product.StandardCost = 20;
        product.ListPrice = 25;
    }

    // 2. 取得所有 Modified 狀態的實體項目
    var entries = context.ObjectStateManager.GetObjectStateEntries(EntityState.Modified);

    foreach (var entry in entries)
    {
        // 顯示實體容器、集合名稱與主鍵
        Console.WriteLine("{0} - {1} - {2} = {3}",
            entry.EntityKey.EntityContainerName,
            entry.EntityKey.EntitySetName,
            entry.EntityKey.EntityKeyValues[0].Key,
            entry.EntityKey.EntityKeyValues[0].Value);

        // 逐欄位比對原始值與目前值
        for (int i = 0; i < entry.OriginalValues.FieldCount; i++)
        {
            Console.WriteLine("\t {0} \u2192 {1}",
                entry.OriginalValues[i],
                entry.CurrentValues[i]);
        }
    }
}

輸出結果範例:

AdventureWorksLTEntities - Product - ProductID = 1004
     1004 \u2192 1004
     def \u2192 def
     abc \u2192 abc
     Black \u2192 Black
     20.0000 \u2192 20
     25.0000 \u2192 25
     ...
     5b2cfc8c-8344-4431-8c2c-651358cce331 \u2192 5b2cfc8c-8344-4431-8c2c-651358cce331

透過此機制,開發者可輕鬆實現審計日誌、異動記錄、或自訂驗證邏輯。

效能優化利器:MergeOption.NoTracking

當資料僅供唯讀顯示(如報表、GridView 列表、下拉選單)且無後續更新需求時,啟用 MergeOption.NoTracking 可大幅降低記憶體佔用與查詢延遲:

  • 實體不被納入 ObjectStateManager 追蹤。
  • 回傳的實體處於 Detached(分離)狀態。
  • 適合 ASP.NET Web Forms、MVC、WPF 等唯讀資料繫結場景。

Entity SQL / ObjectQuery 用法

var sql = "SELECT VALUE m FROM AdventureWorksLTEntities.Model AS m";
var query = new ObjectQuery<Model>(sql, context, MergeOption.NoTracking);

foreach (var m in query)
    Console.WriteLine("{0} {1} {2}", m.ModelID, m.Name, m.ModifiedDate);

LINQ to Entities 用法

LINQ 查詢無法直接傳遞 MergeOption,需對整個 EntitySet 設定:

var categories = from c in context.Category
                 where c.ParentCategory.Name != null
                 orderby c.Name
                 select c;

context.Category.MergeOption = MergeOption.NoTracking; // 設定於 EntitySet 層級
return categories.ToList();

圖示說明

ObjectStateManager 結構示意圖

ObjectStateEntry 內部結構

最佳實務總結

場景建議 MergeOption理由
一般 CRUD 操作AppendOnly (預設)需追蹤變更以支援 SaveChanges
唯讀列表/報表/下拉選單NoTracking避免追蹤開銷,提升查詢效能
需重新附加更新的離線實體NoTracking + 手動 Attach先以 NoTracking 取得,修改後再 Attach 並標記 Modified

善用 ObjectStateManager 與 MergeOption.NoTracking,能讓 EF 應用在功能正確性與執行效能之間取得最佳平衡。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved