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

LINQ to SQL 更新資料效能困境:預設樂觀平行存取機制與替代方案

     編輯:關於C#

LINQ 更新為何「費勁」?

LINQ (Language Integrated Query) 主打查詢優雅,但更新機制預設採用完整欄位樂觀平行存取:

  1. 先 SELECT 取得所有欄位。
  2. 修改實體屬性。
  3. SubmitChanges() 產生 UPDATE,WHERE 條件包含所有原始欄位值。

產生的 SQL 範例

-- 步驟 1:查詢 (全欄位)
SELECT UserId, UserName, FirstName, LastName, CreateTime 
FROM Users WHERE UserId = @p0

-- 步驟 2:更新 (WHERE 含所有原始值)
UPDATE Users SET UserName = @p1
WHERE UserId = @p0 AND UserName = @p2 AND FirstName = @p3 AND LastName = @p4 AND CreateTime = @p5

嚴重後果

  • 效能差:傳輸、比對大量無關欄位。
  • 高併發失敗率:計數器類欄位 (如 Views = Views + 1) 兩次讀寫間若有其他更新,WHERE 條件不符導致 ChangeConflictException,更新失敗。

微軟稱此為 "by design",為防止資料被他人修改後覆蓋。

嘗試替代方案:Attach 方法

多載程式碼結果
Attach(entity)ctx.Users.Attach(newUser);無任何 SQL 產生,無效。
Attach(entity, original)ctx.Users.Attach(newUser, original);拋出 "Cannot add an entity with a key that is already in use."
Attach(entity, true)ctx.Users.Attach(newUser, true);需實體宣告 Version 成員 (Timestamp) 或關閉 UpdateCheck。

解法:加入 Timestamp 欄位或關閉 UpdateCheck

  • 資料表加 RowVersion (Timestamp) 欄位,實體標記 [Column(IsVersion=true)]。
  • 或屬性標記 [Column(UpdateCheck=UpdateCheck.Never)](但重新產生 DBML 會遺失,需手動維護)。

缺點:WHERE 條件仍含 Timestamp,且 DBML 維護麻煩(VS 此版本無「更新模型」功能,需刪除重拖或用 SQLMetal)。

結論與建議

LINQ to SQL 查詢強大,但更新機制在高併發、大表、計數器場景下表現不佳。建議:

  • 複雜/高頻更新改用預存程序或原生 ADO.NET。
  • 僅針對簡單、低併發實體使用 LINQ 更新。
  • 期待後續版本 (Entity Framework Core) 提供更靈活的並發控制。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved