當多個用戶試圖同時修改數據時,需要建立控制機制來防止一個用戶的修改對同時操作的其他用戶所作的修改產生不利的影響。處理這種情況的系統叫做“並發控制”。
並發控制的類型
通常,管理數據庫中的並發有三種常見的方法:
保守式並發控制 - 在從獲取記錄直到記錄在數據庫中更新的這段時間內,該行對用戶不可用。
開放式並發控制 - 只有當實際更新數據時,該行才對其他用戶不可用。更新將在數據庫中檢查該行並確定是否進行了任何更改。如果試圖更新已更改的記錄,則將導致並發沖突。
最后的更新生效 - 只有當實際更新數據時,該行才對其他用戶不可用。但是,不會將更新與初始記錄進行比較;而只是寫出記錄,這可能就改寫了自上次刷新記錄后其他用戶所進行的更改。
保守式並發
保守式並發通常用於兩個目的。第一,在某些情況下,存在對相同記錄的大量爭用。在數據上放置鎖所費的成本小於發生並發沖突時回滾更改所費的成本。
在事務過程中不宜更改記錄的情況下,保守式並發也非常有用。庫存應用程序便是一個很好的示例。假定有一個公司代表正在為一名潛在的客戶檢查庫存。您通常要鎖定記錄,直到生成訂單為止,這通常會將該項標記為“已訂購”狀態並將其從可用庫存中移除。如果未生成訂單,則將釋放該鎖,以便其他檢查庫存的用戶得到准確的可用庫存計數。
但是,在斷開的結構中無法進行保守式並發控制。連接打開的時間只夠讀取數據或更新數據,因此不能長時間地保持鎖。此外,長時間保留鎖的應用程序將無法進行伸縮。
注意 如果基礎數據源支持事務,則可以通過在事務中更新數據來模擬保守式並發。有關更多信息,請參見 ADO.NET 中的事務。
開放式並發
在開放式並發中,只有在訪問數據庫時才設置並保持鎖。這些鎖將防止其他用戶在同一時間更新記錄。除了進行更新這一確切的時刻之外,數據始終可用。有關更多信息,請參見開放式並發。
當試圖更新時,已更改行的初始版本將與數據庫中的現有行進行比較。如果兩者不同,更新將失敗,並引發並發錯誤。這時,將由您使用所創建的業務邏輯來協調這兩行。
最后的更新生效
當使用“最后的更新生效”時,不會對初始數據進行檢查,而只是將更新寫入數據庫。很明顯,可能會發生以下情況:
用戶 A 從數據庫獲取一項記錄。
用戶 B 從數據庫獲取相同的記錄,對其進行修改,然后將更新后的記錄寫回數據庫。
用戶 A 修改“舊”記錄並將其寫回數據庫。
在上述情況中,用戶 A 永遠也不會看到用戶 B 作出的更改。如果您計划使用並發控制的“最后的更新生效”方法,則要確保這種情況是可以接受的。
ADO.NET 和 Visual Studio .NET 中的並發控制
因為數據結構基於斷開的數據,所以 ADO.NET 和 Visual Studio .NET 使用開放式並發。因此,您需要添加業務邏輯,以利用開放式並發解決問題。
如果您選擇使用開放式並發,則可以通過兩種常規的方法來確定是否已發生更改:版本方法(實際版本號或日期時間戳)和保存所有值方法。
版本號方法
在版本號方法中,要更新的記錄必須具有一個包含日期時間戳或版本號的列。當讀取該記錄時,日期時間戳或版本號將保存在客戶端。然后,將對該值進行部分更新。
處理並發的一種方法是僅當 WHERE 子句中的值與記錄上的值匹配時才進行更新。該方法的 SQL 表示形式為:
UPDATE Table1 SET Column1 = @newvalue1, Column2 = @newvalue2
WHERE DateTimeStamp = @origDateTimeStamp
或者,可以使用版本號進行比較:
UPDATE Table1 SET Column1 = @newvalue1, Column2 = @newvalue2
WHERE RowVersion = @origRowVersionValue
如果日期時間戳或版本號匹配,則表明數據存儲區中的記錄未被更改,並且可以安全地使用數據集中的新值對該記錄進行更新。如果不匹配,則將返回錯誤。您可以編寫代碼,在 Visual Studio .NET 中實現這種形式的並發檢查。您還必須編寫代碼來響應任何更新沖突。為了確保日期時間戳或版本號的准確性,您需要在表上設置觸發器,以便在發生對行的更改時,對日期時間戳或版本號進行更新。
保存所有值方法
使用日期時間戳或版本號的替代方法是在讀取記錄時獲取所有字段的副本。ADO.NET 中的 DataSet 對象維護每個修改記錄的兩個版本:初始版本(最初從數據源中讀取的版本)和修改版本(表示用戶更新)。當試圖將記錄寫回數據源時,數據行中的初始值將與數據源中的記錄進行比較。如果它們匹配,則表明數據庫記錄在被讀取后尚未經過更改。在這種情況下,數據集中已更改的值將成功地寫入數據庫。
對於數據適配器的四個命令(DELETE、INSERT、SELECT 和 UPDATE)來說,每個命令都有一個參數集合。每個命令都有用於初始值和當前值(或修改值)的參數。
注意 由於不存在初始記錄,添加新記錄(INSERT 命令)只需要當前值;移除記錄(DELETE 命令)只需要使用初始值來定位要刪除的記錄。
以下示例顯示一個數據集命令的命令文本,該命令更新一個典型的客戶表。該命令是為動態 SQL 和開放式並發而指定的。
UPDATE Customers SET CustomerID = @currCustomerID, CompanyName = @currCompanyName, ContactName = @currContactName, ContactTitle = currContactTitle, Address = @currAddress, City = @currCity, PostalCode = @currPostalCode, Phone = @currPhone, Fax = @currFax WHERE (CustomerID = @origCustomerID) AND (Address = @origAddress OR @origAddress IS NULL AND Address IS NULL) AND (City = @origCity OR @origCity IS NULL AND City IS NULL) AND (CompanyName = @origCompanyName OR @origCompanyName IS NULL AND CompanyName IS NULL) AND (ContactName = @origContactName OR @origContactName IS NULL AND ContactName IS NULL) AND (ContactTitle = @origContactTitle OR @origContactTitle IS NULL AND ContactTitle IS NULL) AND (Fax = @origFax OR @origFax IS NULL AND Fax IS NULL) AND (Phone = @origPhone OR @origPhone IS NULL AND Phone IS NULL) AND (PostalCode = @origPostalCode OR @origPostalCode IS NULL AND PostalCode IS NULL); SELECT CustomerID, CompanyName, ContactName, ContactTitle, Address, City, PostalCode, Phone, Fax FROM Customers WHERE (CustomerID = @currCustomerID)
請注意,九個 SET 語句參數表示將寫入數據庫的當前值,而九個 WHERE 語句參數則表示用於定位初始記錄的初始值。
前九個 SET 語句參數對應於參數集合中的前九個參數。這些參數會將其 SourceVersion 屬性設置為 Current。
接着的九個 WHERE 語句參數用於開放式並發。這些占位符對應於參數集合中接着的九個參數,這些參數的每一個都將其 SourceVersion 屬性設置為 Original。
SELECT 語句用於在發生更新后刷新數據集。它是您在“高級 SQL 生成選項”對話框中設置“刷新數據集”選項時生成的。
注意 上面的 SQL 使用命名參數,而 OleDbDataAdapter 命令則使用問號 (?) 作為參數占位符。
默認情況下,如果您在“數據適配器配置向導”中選擇“開放式並發”選項,Visual Studio 將為您創建這些參數。此時將由您根據自己的業務要求來添加錯誤處理代碼。ADO.NET 提供了一個 DBConcurrencyException 對象,它返回違反並發規則的行。有關更多信息,請參見處理並發錯誤。
參考:http://www.cnblogs.com/badboy2008/articles/499283.html