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

ADO.NET Entity Framework 深度解析(六):樂觀並發控制與衝突解決策略

     編輯:關於.NET

在多使用者併發環境下,資料一致性是核心挑戰。Entity Framework 採用樂觀並發模式,預設不檢查並發衝突,僅在開發者明確標記需並發控制的屬性時,才於更新/刪除的 T-SQL WHERE 子句中加入原始值比對。本文詳細說明如何設定並發模式、攔截衝突異常,並採用適當策略解決。

設定層級並發模式

在 EDM 設計師(或手動修改 CSDL)中,將需參與並發檢查的屬性(如 Color、Timestamp、RowVersion)的 ConcurrencyMode 設為 Fixed:

在 EDM 設計師中設定 ConcurrencyMode=Fixed

並發檢查的 SQL 差異

未設定並發檢查(預設):

UPDATE [SalesLT].[Product]
SET [Color] = @0, [StandardCost] = @1, [ListPrice] = @2
WHERE ([ProductID] = @3)
-- 僅主鍵比對,最後寫入者獲勝

設定 Color 為 Fixed 後:

UPDATE [SalesLT].[Product]
SET [Color] = @0, [StandardCost] = @1, [ListPrice] = @2
WHERE (([ProductID] = @3) AND ([Color] = @4))
-- WHERE 子句多了原始 Color 值比對,若資料庫值已被他人修改,影響筆數為 0,拋出異常

攔截並解決 OptimisticConcurrencyException

當 SaveChanges() 偵測到影響筆數為 0,會拋出 OptimisticConcurrencyException。透過 ObjectContext.Refresh() 可重新同步實體狀態:

public void UpdateProductWithConcurrencyHandling()
{
    // Context1 讀取並修改
    var product1 = context.Product.FirstOrDefault(p => p.ProductID == 1004);
    if (product1 != null)
    {
        product1.Color = "Black";
        product1.StandardCost = 20;
        product1.ListPrice = 25;
    }

    // 模擬另一個 Context 同步修改同一筆資料
    using (var context2 = new AdventureWorksLTEntities())
    {
        var product2 = context2.Product.FirstOrDefault(p => p.ProductID == 1004);
        if (product2 != null)
        {
            product2.Color = "Blue";
            product2.StandardCost = 18;
            product2.ListPrice = 30;
        }

        // Context1 先存檔成功
        context.SaveChanges();

        try
        {
            // Context2 存檔將拋出 OptimisticConcurrencyException
            context2.SaveChanges();
        }
        catch (OptimisticConcurrencyException ex)
        {
            Console.WriteLine("偵測到並發衝突:" + ex.Message);

            // 策略一:ClientWins(以用戶端修改值覆蓋資料庫,最後寫入者獲勝)
            context2.Refresh(RefreshMode.ClientWins, product2);
            context2.SaveChanges(); // 再次嘗試存檔

            // 策略二:StoreWins(放棄用戶端修改,重新載入資料庫最新值)
            // context2.Refresh(RefreshMode.StoreWins, product2);
            // 此時 product2 屬性值已同步為資料庫最新值,UI 可提示使用者重新編輯
        }
    }
}

兩種 RefreshMode 的行為差異

模式行為適用場景
RefreshMode.ClientWins 保留記憶體中實體的修改值,將原始值更新為資料庫最新值,再次 SaveChanges 會以用戶端值覆蓋資料庫。 「最後寫入者獲勝」邏輯,適合低衝突、用戶可接受覆蓋他人修改的簡單應用。
RefreshMode.StoreWins 捨棄記憶體中實體的修改,完全重新載入資料庫最新值覆蓋原始值與目前值。 需提示使用者「資料已被他人修改,請重新編輯」的嚴謹業務流程。

生產環境建議的衝突處理流程

單純用 ClientWins 或 StoreWins 都可能導致資料遺失。最佳實務建議:

  1. 捕獲 OptimisticConcurrencyException。
  2. 採用 StoreWins 重新載入最新資料。
  3. 在 UI 層提示使用者:「此資料已被其他人修改,已為您重新載入最新版本,請確認後再儲存。」
  4. 使用者重新編輯並提交,直到無衝突為止。

此流程確保資料完整性,同時尊重使用者操作意圖,是企業級應用處理並發的標準模式。

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