LINQ to SQL語句(13)之開放式併發控制和事務

Simultaneous Changes開放式併發控制

下表介紹 LINQ to SQL 文檔中涉及開放式併發的術語:數據庫

術語 說明
併發 兩個或更多用戶同時嘗試更新同一數據庫行的情形。
併發衝突 兩個或更多用戶同時嘗試向一行的一列或多列提交衝突值的情形。
併發控制 用於解決併發衝突的技術。
開放式併發控制 先調查其餘事務是否已更改了行中的值,再容許提交更改的技術。相比之下,保守式併發控制則是經過鎖定記錄來避免發生併發衝突。之因此稱做開放式控制,是由於它將一個事務干擾另外一事務視爲不太可能發生。
衝突解決 經過從新查詢數據庫刷新出現衝突的項,而後協調差別的過程。刷新對象時,LINQ to SQL 更改跟蹤器會保留如下數據: 最初從數據庫獲取並用於更新檢查的值 經過後續查詢得到的新數據庫值。  LINQ to SQL 隨後會肯定相應對象是否發生衝突(即它的一個或多個成員值是否已發生更改)。若是此對象發生衝突,LINQ to SQL 下一步會肯定它的哪些成員發生衝突。LINQ to SQL 發現的任何成員衝突都會添加到衝突列表中。

在 LINQ to SQL 對象模型中,當如下兩個條件都獲得知足時,就會發生「開放式併發衝突」:客戶端嘗試向數據庫提交更改;數據庫中的一個或多個更新檢查值自客戶端上次讀取它們以來已獲得更新。 此衝突的解決過程包括查明對象的哪些成員發生衝突,而後決定您但願如何進行處理。服務器

開放式併發(Optimistic Concurrency)

說明:這個例子中在你讀取數據以前,另一個用戶已經修改並提交更新了這個數據,因此不會出現衝突。併發

//咱們打開一個新的鏈接來模擬另一個用戶
NorthwindDataContext otherUser_db = new NorthwindDataContext();
var otherUser_product =
    otherUser_db.Products.First(p => p.ProductID == 1);
otherUser_product.UnitPrice = 999.99M;
otherUser_db.SubmitChanges();
//咱們當前鏈接
var product = db.Products.First(p => p.ProductID == 1);
product.UnitPrice = 777.77M;
try
{
    db.SubmitChanges();//當前鏈接執行成功
}
catch (ChangeConflictException)
{
}

說明:咱們讀取數據以後,另一個用戶獲取並提交更新了這個數據,這時,咱們更新這個數據時,引發了一個併發衝突。系統發生回滾,容許你能夠從數據庫檢索新更新的數據,並決定如何繼續進行您本身的更新。spa

//當前用戶
var product = db.Products.First(p => p.ProductID == 1);
//咱們打開一個新的鏈接來模擬另一個用戶
NorthwindDataContext otherUser_db = new NorthwindDataContext() ;
var otherUser_product = 
    otherUser_db.Products.First(p => p.ProductID == 1);
otherUser_product.UnitPrice = 999.99M;
otherUser_db.SubmitChanges();
//當前用戶修改
product.UnitPrice = 777.77M;
try
{
    db.SubmitChanges();
}
catch (ChangeConflictException)
{
    //發生異常!
}

Transactions事務

LINQ to SQL 支持三種事務模型,分別是:code

  • 顯式本地事務:調用 SubmitChanges 時,若是 Transaction 屬性設置爲事務,則在同一事務的上下文中執行 SubmitChanges 調用。成功執行事務後,要由您來提交或回滾事務。與事務對應的鏈接必須與用於構造 DataContext 的鏈接匹配。若是使用其餘鏈接,則會引起異常。
  • 顯式可分發事務:能夠在當前 Transaction 的做用域中調用 LINQ to SQL API(包括但不限於 SubmitChanges)。LINQ to SQL 檢測到調用是在事務的做用域內,於是不會建立新的事務。在這種狀況下,<token>vbtecdlinq</token> 還會避免關閉鏈接。您能夠在此類事務的上下文中執行查詢和 SubmitChanges 操做。
  • 隱式事務:當您調用 SubmitChanges 時,LINQ to SQL 會檢查此調用是否在 Transaction 的做用域內或者 Transaction 屬性是否設置爲由用戶啓動的本地事務。若是這兩個事務它均未找到,則 LINQ to SQL 啓動本地事務,並使用此事務執行所生成的 SQL 命令。當全部 SQL 命令均已成功執行完畢時,LINQ to SQL 提交本地事務並返回。

1.Implicit(隱式)

說明:這個例子在執行SubmitChanges()操做時,隱式地使用了事務。由於在更新2種產品的庫存數量時,第二個產品庫存數量爲負數了,違反了服務器上的 CHECK 約束。這致使了更新產品所有失敗了,系統回滾到這個操做的初始狀態。對象

try
{
    Product prod1 = db.Products.First(p => p.ProductID == 4);
    Product prod2 = db.Products.First(p => p.ProductID == 5);
    prod1.UnitsInStock -= 3;
    prod2.UnitsInStock -= 5;//錯誤:庫存數量的單位不能是負數
    //要麼所有成功要麼所有失敗
    db.SubmitChanges();
}
catch (System.Data.SqlClient.SqlException e)
{
    //執行異常處理
}

2.Explicit(顯式)

說明:這個例子使用顯式事務。經過在事務中加入對數據的讀取以防止出現開放式併發異常,顯式事務能夠提供更多的保護。如同上一個查詢中,更新 prod2 的 UnitsInStock 字段將使該字段爲負值,而這違反了數據庫中的 CHECK 約束。這致使更新這兩個產品的事務失敗,此時將回滾全部更改。 blog

using (TransactionScope ts = new TransactionScope())
{
    try
    {
        Product prod1 = db.Products.First(p => p.ProductID == 4);
        Product prod2 = db.Products.First(p => p.ProductID == 5);
        prod1.UnitsInStock -= 3;
        prod2.UnitsInStock -= 5;//錯誤:庫存數量的單位不能是負數
        db.SubmitChanges();
    }
    catch (System.Data.SqlClient.SqlException e)
    {
        //執行異常處理
    }
}
相關文章
相關標籤/搜索