在Linq to Sql中管理并发更新时的冲突(3):使用记录的时间戳进行检测
生活随笔
收集整理的這篇文章主要介紹了
在Linq to Sql中管理并发更新时的冲突(3):使用记录的时间戳进行检测
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
我們描述了Linq to Sql檢測在更新時是否產(chǎn)生了沖突的基本方法:將該記錄每個字段原來的值和更新時的值進(jìn)行對比,如果稍有不同則意味著記錄被修改過,因此產(chǎn)生了更新沖突。不過您是否有這樣的感覺,這種方法實在累贅了一些?如果一個表中有數(shù)十個字段,那么更新就必須完整地檢測一遍(不過我會在今后的文章中提到這方面的控制)。再者,如果其中某一個字段儲存了洋洋灑灑上萬字的文章,那么在驗證時僅僅是將它從Web服務(wù)器發(fā)送到數(shù)據(jù)庫服務(wù)器就需要耗費可觀的帶寬與時間,這是不是顯得有些“得不償失”呢? 因此Linq to Sql提供了另外一種檢測并發(fā)更新沖突的方式:使用記錄的時間戳。這并不是Linq to Sql特有的功能,如果您了解其他的ORM框架的話,就會發(fā)現(xiàn)諸如Hibernate也提供了類似的機制——自然,在使用上不會像Linq to Sql那樣方便。 在Sql Server中設(shè)計數(shù)據(jù)表時,我們可以使用一個特殊的數(shù)據(jù)類型:timestamp。請不要將它與SQL-2003標(biāo)準(zhǔn)中的timestamp類型混淆起來,那里的timestamp和Sql Server中的datetime比較相似(Oracle中timestamp的概念符合SQL-2003標(biāo)準(zhǔn),而MySql中timestamp的概念與Sql Server相同),而Sql Server中的timestamp與SQL-2003標(biāo)準(zhǔn)中的rowversion類型對應(yīng)。Sql Server中的timestamp類型和binary(8)在存儲上非常類似(不過nullable的timestamp和nvarchar(8)類似),從類型名稱上我們就可以看出,這是一個“時間戳”字段:當(dāng)數(shù)據(jù)表中的某一條記錄被添加或者修改之后,Sql Server會自動向類型為timestamp的字段寫入當(dāng)前時間。換句話說,只要在更新時發(fā)現(xiàn)該字段的值沒有被修改過,就表明沒有產(chǎn)生并發(fā)沖突。 我們還是通過一個例子來體驗一下吧。
如上圖。我們定義了一個新的數(shù)據(jù)表,其中有個record_version字段為timestamp類型,這就是記錄的時間戳(record_version這個字段名似乎有點不太“雅觀”,我覺得我們不會去主動使用它,所以問題不大——當(dāng)然一些靜態(tài)檢查工具可不這么認(rèn)為:))。有了記錄的時間戳,我們就可以在檢測更新沖突時獲得更好的性能了。 try
{
LinqToSqlDemoDataContext?dataContext =?new?LinqToSqlDemoDataContext();
Order?order = dataContext.Orders.Single(o => o.OrderID == 1);
order.Name =?"New Order Name";
??? dataContext.Log =?Console.Out;
????//?在下面的語句上設(shè)置一個斷點
dataContext.SubmitChanges();
}
catch(ChangeConflictException?e)
{
Console.WriteLine(e.Message);
}
Console.ReadLine(); 在最后的語句上設(shè)置斷點,并且在程序運行至斷點后去數(shù)據(jù)庫里對OrderID為1的紀(jì)錄作任意更新。然后按F5繼續(xù)運行: 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 (Size = 0; Prec = 0; Scale = 0) [1]
-- @p1: Input Timestamp (Size = 8; Prec = 0; Scale = 0) [SqlBinary(8)]
-- @p2: Input NVarChar (Size = 14; Prec = 0; Scale = 0) [New Order Name]
-- @p3: Input Int (Size = 0; Prec = 0; Scale = 0) [1]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.21022.8
Row not found or changed. 上面代碼中的UPDATE語句相信大家都很清楚其含義。不過這里可能還需要解釋其他兩個問題: 首先是那句SELECT語句。如果您去閱讀自動生成的Object Model的代碼時就會發(fā)現(xiàn),record_version屬性上有一個ColumnAttribute標(biāo)記(假設(shè)您使用了Attribute Based Mapping Source),其AutoSync屬性為Always,因此在任何操作之后,Linq to Sql都會補充一句SELECT語句,以此獲得新的數(shù)據(jù)并修改DataContext中的特定對象。其次,由于timestamp類型的數(shù)據(jù)在記錄被修改時就會設(shè)置,因此在更新時其他紀(jì)錄的值與之前相同,也會引發(fā)更新沖突,這一點和基于字段值比較的前一種方法是不同的。 那么,我們一直說出現(xiàn)了“并發(fā)更新沖突”,那么發(fā)生沖突后又會出現(xiàn)什么問題呢?我們來看一個略有些復(fù)雜的示例: 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);
Console.WriteLine('Order 1: '?+ order1.Introduction);
Console.WriteLine('Order 2: '?+ order2.Introduction);
Console.WriteLine('Order 3: '?+ order3.Introduction);
Console.WriteLine();
order1.Introduction =?'Order 1 modified.';
order2.Introduction =?'Order 2 modified.';
order3.Introduction =?'Order 3 modified.';
dataContext.Log =?Console.Out;
//?在下面的語句上設(shè)置一個斷點
dataContext.SubmitChanges();
}
catch(ChangeConflictException?e)
{
Console.WriteLine('---------- '?+ e.Message +?' ----------');
}
LinqToSqlDemoDataContextdb =?new?LinqToSqlDemoDataContext();
Ordero1 = db.Orders.Single(o => o.OrderID == 1);
Ordero2 = db.Orders.Single(o => o.OrderID == 2);
Ordero3 = db.Orders.Single(o => o.OrderID == 3);
Console.WriteLine('Order 1: '?+ o1.Introduction);
Console.WriteLine('Order 2: '?+ o2.Introduction);
Console.WriteLine('Order 3: '?+ o3.Introduction);
Console.ReadLine(); 假設(shè)我們的數(shù)據(jù)表里有以下三條記錄:
當(dāng)程序進(jìn)入到SubmitChanges語句的斷點時,我們?nèi)?shù)據(jù)庫中運行以下代碼,以修改OrderID為2的記錄。 UPDATE?Order?SET?OrderID =?"New Order 2"?WHERE?OrderID = 2 繼續(xù)運行程序,最終控制臺中會打印出以下信息: Order 1: This is order 1
Order 2: This is order 2
Order 3: This is order 3
UPDATE [dbo].[Order]
SET [Introduction] = @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 (Size = 0; Prec = 0; Scale = 0) [1]
-- @p1: Input Timestamp (Size = 8; Prec = 0; Scale = 0) [SqlBinary(8)]
-- @p2: Input NVarChar (Size = 26; Prec = 0; Scale = 0) [Order 1 modified.]
-- @p3: Input Int (Size = 0; Prec = 0; Scale = 0) [1]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.21022.8
UPDATE [dbo].[Order]
SET [Introduction] = @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 (Size = 0; Prec = 0; Scale = 0) [2]
-- @p1: Input Timestamp (Size = 8; Prec = 0; Scale = 0) [SqlBinary(8)]
-- @p2: Input NVarChar (Size = 26; Prec = 0; Scale = 0) [Order 2 modified.]
-- @p3: Input Int (Size = 0; Prec = 0; Scale = 0) [2]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.21022.8
---------- 1 of 2 updates failed. ----------
Order 1: This is order 1
Order 2: This is order 2
Order 3: This is order 3 首先我們分別打印出三個Video對象的Introduction并將它們修改為新的值。在SubmitChanges方法調(diào)用之前,數(shù)據(jù)庫中ID為2的記錄已經(jīng)被修改過了,因此在第一組UPDATE+SELECT調(diào)用成功之后——請注意,這是一次調(diào)用,Linq to Sql每次更新一條記錄——在更新第二條記錄之后發(fā)現(xiàn)了并發(fā)沖突。于是拋出異常(請注意異常的Message表示“兩次更新其中有一次失敗了”),第三條記錄也不會再更新了。在沖突發(fā)生之后,ID為2和紀(jì)錄自然沒有被修改(WHERE條件不成立),但是第一條記錄呢?從try...catch塊之后的操作中看,ID為1的記錄也沒有被更新。 也就是說,第一次更新被回滾了。這自然是事務(wù)的作用。在調(diào)用(默認(rèn)的)SubmitChanges方法時,Linq to Sql會把所有的更新放在同一個事務(wù)中,因此它們“共同進(jìn)退”。但是由于業(yè)務(wù)需求不同,有時候我們不希望某條記錄的沖突導(dǎo)致了所有更新失敗。自然,Linq to Sql也提供了這個方面的控制。在下一篇文章中,我們就來看一下Linq to Sql中與樂觀并發(fā)控制有關(guān)的事務(wù)問題,以及出現(xiàn)并發(fā)沖突之后的解決方式。
本文轉(zhuǎn)自 jeffz 51CTO博客,原文鏈接:http://blog.51cto.com/jeffz/59516,如需轉(zhuǎn)載請自行聯(lián)系原作者
{
LinqToSqlDemoDataContext?dataContext =?new?LinqToSqlDemoDataContext();
Order?order = dataContext.Orders.Single(o => o.OrderID == 1);
order.Name =?"New Order Name";
??? dataContext.Log =?Console.Out;
????//?在下面的語句上設(shè)置一個斷點
dataContext.SubmitChanges();
}
catch(ChangeConflictException?e)
{
Console.WriteLine(e.Message);
}
Console.ReadLine(); 在最后的語句上設(shè)置斷點,并且在程序運行至斷點后去數(shù)據(jù)庫里對OrderID為1的紀(jì)錄作任意更新。然后按F5繼續(xù)運行: 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 (Size = 0; Prec = 0; Scale = 0) [1]
-- @p1: Input Timestamp (Size = 8; Prec = 0; Scale = 0) [SqlBinary(8)]
-- @p2: Input NVarChar (Size = 14; Prec = 0; Scale = 0) [New Order Name]
-- @p3: Input Int (Size = 0; Prec = 0; Scale = 0) [1]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.21022.8
Row not found or changed. 上面代碼中的UPDATE語句相信大家都很清楚其含義。不過這里可能還需要解釋其他兩個問題: 首先是那句SELECT語句。如果您去閱讀自動生成的Object Model的代碼時就會發(fā)現(xiàn),record_version屬性上有一個ColumnAttribute標(biāo)記(假設(shè)您使用了Attribute Based Mapping Source),其AutoSync屬性為Always,因此在任何操作之后,Linq to Sql都會補充一句SELECT語句,以此獲得新的數(shù)據(jù)并修改DataContext中的特定對象。其次,由于timestamp類型的數(shù)據(jù)在記錄被修改時就會設(shè)置,因此在更新時其他紀(jì)錄的值與之前相同,也會引發(fā)更新沖突,這一點和基于字段值比較的前一種方法是不同的。 那么,我們一直說出現(xiàn)了“并發(fā)更新沖突”,那么發(fā)生沖突后又會出現(xiàn)什么問題呢?我們來看一個略有些復(fù)雜的示例: 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);
Console.WriteLine('Order 1: '?+ order1.Introduction);
Console.WriteLine('Order 2: '?+ order2.Introduction);
Console.WriteLine('Order 3: '?+ order3.Introduction);
Console.WriteLine();
order1.Introduction =?'Order 1 modified.';
order2.Introduction =?'Order 2 modified.';
order3.Introduction =?'Order 3 modified.';
dataContext.Log =?Console.Out;
//?在下面的語句上設(shè)置一個斷點
dataContext.SubmitChanges();
}
catch(ChangeConflictException?e)
{
Console.WriteLine('---------- '?+ e.Message +?' ----------');
}
LinqToSqlDemoDataContextdb =?new?LinqToSqlDemoDataContext();
Ordero1 = db.Orders.Single(o => o.OrderID == 1);
Ordero2 = db.Orders.Single(o => o.OrderID == 2);
Ordero3 = db.Orders.Single(o => o.OrderID == 3);
Console.WriteLine('Order 1: '?+ o1.Introduction);
Console.WriteLine('Order 2: '?+ o2.Introduction);
Console.WriteLine('Order 3: '?+ o3.Introduction);
Console.ReadLine(); 假設(shè)我們的數(shù)據(jù)表里有以下三條記錄:
| OrderID | Name | Introduction | record_version |
| 1 | Order 1 | This is order 1 | <Binary data> |
| 2 | Order 2 | This is order 2 | <Binary data> |
| 3 | Order 3 | This is order 3 | <Binary data> |
Order 2: This is order 2
Order 3: This is order 3
UPDATE [dbo].[Order]
SET [Introduction] = @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 (Size = 0; Prec = 0; Scale = 0) [1]
-- @p1: Input Timestamp (Size = 8; Prec = 0; Scale = 0) [SqlBinary(8)]
-- @p2: Input NVarChar (Size = 26; Prec = 0; Scale = 0) [Order 1 modified.]
-- @p3: Input Int (Size = 0; Prec = 0; Scale = 0) [1]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.21022.8
UPDATE [dbo].[Order]
SET [Introduction] = @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 (Size = 0; Prec = 0; Scale = 0) [2]
-- @p1: Input Timestamp (Size = 8; Prec = 0; Scale = 0) [SqlBinary(8)]
-- @p2: Input NVarChar (Size = 26; Prec = 0; Scale = 0) [Order 2 modified.]
-- @p3: Input Int (Size = 0; Prec = 0; Scale = 0) [2]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.21022.8
---------- 1 of 2 updates failed. ----------
Order 1: This is order 1
Order 2: This is order 2
Order 3: This is order 3 首先我們分別打印出三個Video對象的Introduction并將它們修改為新的值。在SubmitChanges方法調(diào)用之前,數(shù)據(jù)庫中ID為2的記錄已經(jīng)被修改過了,因此在第一組UPDATE+SELECT調(diào)用成功之后——請注意,這是一次調(diào)用,Linq to Sql每次更新一條記錄——在更新第二條記錄之后發(fā)現(xiàn)了并發(fā)沖突。于是拋出異常(請注意異常的Message表示“兩次更新其中有一次失敗了”),第三條記錄也不會再更新了。在沖突發(fā)生之后,ID為2和紀(jì)錄自然沒有被修改(WHERE條件不成立),但是第一條記錄呢?從try...catch塊之后的操作中看,ID為1的記錄也沒有被更新。 也就是說,第一次更新被回滾了。這自然是事務(wù)的作用。在調(diào)用(默認(rèn)的)SubmitChanges方法時,Linq to Sql會把所有的更新放在同一個事務(wù)中,因此它們“共同進(jìn)退”。但是由于業(yè)務(wù)需求不同,有時候我們不希望某條記錄的沖突導(dǎo)致了所有更新失敗。自然,Linq to Sql也提供了這個方面的控制。在下一篇文章中,我們就來看一下Linq to Sql中與樂觀并發(fā)控制有關(guān)的事務(wù)問題,以及出現(xiàn)并發(fā)沖突之后的解決方式。
本文轉(zhuǎn)自 jeffz 51CTO博客,原文鏈接:http://blog.51cto.com/jeffz/59516,如需轉(zhuǎn)載請自行聯(lián)系原作者
總結(jié)
以上是生活随笔為你收集整理的在Linq to Sql中管理并发更新时的冲突(3):使用记录的时间戳进行检测的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 小五思科技术学习笔记之SSH
- 下一篇: GridView行号大集合