前文介紹 LINQ to SQL 透過比對每個欄位原始值檢測並發衝突,但此法有缺點:欄位多時檢測繁瑣,大型文字欄位 (如文章內容) 驗證時須傳輸龐大資料,頗為「得不償失」。
LINQ to SQL 提供替代方案:使用資料列版本戳記。這非 LINQ to SQL 專有,Hibernate 等 ORM 亦有類似機制。
設計資料表時可使用 timestamp 類型。勿與 SQL-2003 標準 timestamp (類似 datetime) 混淆,SQL Server timestamp 對應 SQL-2003 rowversion,儲存類似 binary(8)。每當資料列新增或修改,SQL Server 自動寫入當前時間戳記。更新時若發現該欄位值未變,即表示無並發衝突。
try
{
LinqToSqlDemoDataContext dataContext = new LinqToSqlDemoDataContext();
Order order = dataContext.Orders.Single(o => o.OrderID == 1);
order.Name = "New Order Name";
dataContext.Log = Console.Out;
// 在下行設中斷點
dataContext.SubmitChanges();
}
catch (ChangeConflictException e)
{
Console.WriteLine(e.Message);
}
Console.ReadLine();
中斷點處在資料庫修改 OrderID=1 的記錄,繼續執行產生 SQL:
UPDATE [dbo].[Order]
SET [Name] = @p2
WHERE ([OrderID] = @p0) AND ([record_version] = @p1)
SELECT [t1].[record_version]
FROM [dbo].[Order] AS [t1]
WHERE ((@@ROWCOUNT) > 0) AND ([t1].[OrderID] = @p3)
-- @p0: Input Int [1]
-- @p1: Input Timestamp [SqlBinary(8)]
-- @p2: Input NVarChar [New Order Name]
-- @p3: Input Int [1]
WHERE 條件包含 record_version,僅比對此欄位而非所有欄位SELECT 取回新 timestamp:因 ColumnAttribute 標記 AutoSync = Always,操作後自動同步try
{
LinqToSqlDemoDataContext dataContext = new LinqToSqlDemoDataContext();
Order order1 = dataContext.Orders.Single(o => o.OrderID == 1);
Order order2 = dataContext.Orders.Single(o => o.OrderID == 2);
Order order3 = dataContext.Orders.Single(o => o.OrderID == 3);
// 列印原始 Introduction...
order1.Introduction = "Order 1 modified.";
order2.Introduction = "Order 2 modified.";
order3.Introduction = "Order 3 modified.";
dataContext.Log = Console.Out;
// 中斷點
dataContext.SubmitChanges();
}
catch (ChangeConflictException e)
{
Console.WriteLine("---------- " + e.Message + " ----------");
}
// 重新查詢驗證資料庫狀態...
中斷點處修改 OrderID=2 的記錄,執行結果:
Order 1: This is order 1
Order 2: This is order 2
Order 3: This is order 3
UPDATE ... OrderID=1 成功
UPDATE ... OrderID=2 失敗 (timestamp 不符)
---------- 1 of 2 updates failed. ----------
Order 1: This is order 1
Order 2: This is order 2
Order 3: This is order 3
SubmitChanges 將所有更新置於同一交易,「共同進退」業務需求可能不希望單筆衝突導致全盤失敗。LINQ to SQL 提供交易控制與衝突解決機制,下文將深入探討樂觀並發控制相關交易議題與衝突解決方式。