在Linq to Sql中管理并發更新時的沖突(1):預備知識
無論與目前的ORM框架相比有沒有優勢,Linq to Sql在語言和平臺的級別上為我們提供了一種新的操作對象和數據的方式,在一定程度上為我們解決了Object != Data的問題。在實際應用中,對于數據庫的操作往往有著天生的并發性,因此在更新數據時可能會產生沖突。有些時候,如果沒有合理的解決沖突問題,輕則讓用戶摸不著頭腦,重則讓系統數據處于一種不一致的狀態。Linq to Sql自然考慮到了這一點,本系列討論的內容,就是在使用Linq to Sql時,如何管理并發更新時產生的沖突。
本文為這個系列的第一篇,將討論一些預備知識,它們是進行后續研究的基礎。
一些定義:
首先,我們來看一些定義:
- 并發(Concurrency):兩個或更多的用戶嘗試同時更新數據庫的同一條記錄。
- 并發沖突(Concurrency Confilct):兩個或更多的用戶嘗試同時向同一條記錄的一個或多個字段提交沖突的值。
- 并發控制(Concurrency Control):解決并發沖突的技術。
- 樂觀并發控制(Optimistic Concurrency Control):在提交當前事務之前,首先查看即將更新的記錄是否被別的事務所改變的一種技術。
- 悲觀并發控制(Pessimistic Concurrency Control):為紀錄加鎖以阻止其他事務訪問某些記錄,以避免產生并發沖突的一種技術。
Linq to Sql的對象模型使用樂觀并發控制的方式來發現和解決沖突問題。很顯然,它假設沖突發生的可能性并不大。如果您需要使用悲觀并發控制來解決沖突問題,則可以使用其他方法(例如自定義存儲過程供程序調用)。
調試方法:
Linq to Sql的相當部分由編譯器來實現,而語言中的Linq語句最終會被轉化為Sql,因此如果要理解Linq to Sql的工作,一定要將操作中所執行的Sql語句給挖掘出來。一般來說,要挖掘出操作中所使用的Sql語句,可以使用以下幾種方法(以下將使用Sql Server 2005自帶的AdventureWorks數據庫來作為示例):
1、獲取Query所對應的SqlCommand對象:
在開發過程中,我們可以通過Query獲得對應的Sql Command對象。請看如下代碼:
AdventureWorksDataContext db = new AdventureWorksDataContext(); var products = from p in db.Products where p.ProductID == 3 select new { p.ProductID, p.Name }; foreach (var p in products) { Console.WriteLine(p); } DbCommand cmd = db.GetCommand(products); Console.WriteLine("------------"); Console.WriteLine("Command Text: \n{0}", cmd.CommandText); Console.WriteLine("------------"); Console.WriteLine("Command Type: \n{0}", cmd.CommandType); Console.WriteLine("------------"); Console.WriteLine("Command Parameters:"); foreach (DbParameter p in cmd.Parameters) { Console.WriteLine("{0}: {1}", p.ParameterName, p.Value); } Console.ReadLine();
輸出結果如下:
Command Text: SELECT [t0].[ProductID], [t0].[Name] FROM [Production].[Product] AS [t0] WHERE [t0].[ProductID] = @p0 ------------ Command Type: Text ------------ Command Parameters: @p0: 3
可以看到,無論是Sql語句或是參數都被打印了出來。事實上,由于我們得到了完整的SqlCommand對象,我們可以獲取的信息并不止上述這些。
2、使用LINQ to SQL Debug Visualizer:
使用LINQ to SQL Debug Visiualizer,我們可以在調試程序時直觀地獲得Query所對應的Sql語句以及參數,而不必獲得SqlCommand對象并打印信息。具體使用方法詳見Scott Gu的這篇博文。