C#——細說事務(下)


目錄

一、事務的定義

二、事務管理器

三、在ADO.NET中實現事務

四、隱式事務 TransactionScope

五、在WCF中實現事務

六、嵌套式事務

七、異步事務

六、分布式隱式事務

嵌套式事務經常會出現在項目中,但往往容易被大家忽略,下面介紹一下 嵌套式事務的用法:(該事例可以說明很多問題)

使用分布式事務必須開啟服務:Distributed Transaction Coordinator 否則報錯  “服務器 'MRWANG' 上的 MSDTC 不可用

//分布在不同數據庫之間的事務操作
using(Connection con1=new Connection("連接字符串1"))
using (TransactionScope scope1 = new TransactionScope())            
 {
        //............scope1事務處理操作        ///--此處提交將和外層(scope1)TransactionScope無關--
       //在無事務環境中執行操作,取消環境事務(TransactionScopeOption.Suppress)
            //每次進入創建新的事務環境(TransactionScopeOption.RequiresNew)          

        ///--scope2提交將和外層(scope1)TransactionScope有密切關聯scope2提交成功,外層遇到異常,則全部回滾,因為屬於同一個事務環境--
        //每次進入將檢測是否存在事務環境,若存在則使用,不存在則重新創建(TransactionScopeOption.Required)

      using(Connection con2=new Connection("連接字符串2"))
        using (TransactionScope scope2=new TransactionScope(TransactionScopeOption.Required))
        {
        //............scope2事務處理操作
       scope2.Complete(); //只完成嵌套式的內部事務,但事務並未正式提交
} scope1.Complete(); //代表完成所有事務,事務正式提交
}

一 般項目中,大家都只會把事務用在DAL層,用於管理數據的CRUD,但其實在一些操作中,某些數據的操作必須具有一致性。比如在訂單管理中,當插入一條 OrderItem時,Order表內的總體價格,商品數量等也會隨之改變。很多人把兩個表的操作合成一個方法,放在OrderDAL中完成。但其實這樣 做違返設計的原則,因為計算Order的總體價格時可能會包含商品優惠、客戶等級、客戶積分等等業務邏輯,而在DAL層不應該包含任何的業務邏輯存在的, 所以這樣操作應該放在業務層完成。這時候,業務層的方法內就需要同時調用OrderItemDAL的AddOrderItem(OrderItem) 方法和OrderDAL的UpdateOrder(Order)方法,為了保證數據的一致性更新,就需要使用嵌套式事務。但這往往容易被開發人員所忽略, 當Order表的更新成功而OrderItem表的插入失敗時,系統不能保證數據的同步回滾,那就會造成數據的邏輯性錯誤。

下面的例子就是為了保證數據一致性更新而使用的嵌套式事務,在使用嵌套式事務的時候要應該注意及其把對象釋放,避免造成死鎖

 1 namespace DAL
 2 {     
 3      public class OrderDAL
 4      {
 5          public void UpdateOrder(Order order)
 6          {
 7              using (TransactionScope scope = new TransactionScope())
 8              {
 9                   ......         
10                   scope.Complete();
11              }
12          }
13      }
14  
15      public class OrderItemDAL
16      {
17          public void AddOrderItem(OrderItem orderItem)
18          {
19              using (TransactionScope scope = new TransactionScope())
20              {
21                  ......
22                  scope.Complete();
23              }
24          }
25      }
26  }
27 
28 namespace BLL
29 {
30      public class OrderManager
31      {
32          public void AddOrderItem(OrderItem item)
33          {
34              using (TransactionScope scope = new TransactionScope())
35              {
36                  OrderItemDAL orderItemDAL=new OrderItemDAL();
37                  orderItemDAL.AddOrderItem(item);
38                  OrderDAL orderDAL=new OrderDAL();
39                  ........
40                  orderDAL.UpdateOrder(order);
41                  scope.Complete();
42              }
43          }
44      }
45 }

回到目錄

 

七、異步事務

記得在第二節的時候曾經提起過事務類Transaction的方法中包含方法

public DependentTransaction DependentClone(DependentCloneOption)

此方法作用是克隆當前的事務,它在多線程調用同一事務的情況下使用經常使用。其中DependentCloneOption包含有兩個選項:

一為BlockCommitUntilComplete,這表示在依賴事務未完成前,事務將處於阻塞狀態,只有在所有依賴事務完成后,事務才能執行提交;

二為RollbackInNotComplete,這表示依賴事務必須在事務完成前調用Complete(),否則事務會被視為失敗。

 

在 普通情況下,事務都會通過Transaction.Current 來獲取,但此方法只能獲取當前線程下的事務對象,在異步方法當中,這只會返回一個空值 null 。此時就需要使用DependentClone 方法獲取依賴事務對象 DependentTransaction ,再把此對象作為參數傳遞到回調函數中。

 1      class Program
 2      {
 3          static void Main(string[] args)
 4          {
 5              Method();
 6              Console.ReadKey();
 7          }
 8  
 9          static void Method()
10          {
11              using (TransactionScope scope = new TransactionScope())
12              {
13                  ShowMessage("Main Thread");
14  
15                  //獲取一個依賴事務,把依賴事務作為回調參數傳到回調函數中
16 DependentTransaction dependentTransaction= 17 Transaction.Current.DependentClone(DependentCloneOption.BlockCommitUntilComplete); 18 ThreadPool.QueueUserWorkItem(new WaitCallback(AsyncThread), dependentTransaction); 19 ........ 20 scope.Complete(); //完成主線程事務,在依賴事務完成前,事務提交將處於阻塞狀態 21 } 22 } 23 24 static void AsyncThread(object transaction) 25 { 26 //獲取依賴事務,利用TransactionScope(Transaction)構造函數生成隱式事務
27 DependentTransaction dependentTransaction = (DependentTransaction)transaction; 28 using (TransactionScope scope = new TransactionScope(dependentTransaction)) 29 { 30 ShowMessage("AsyncThread"); 31 .......... 32 scope.Complete(); //完成異步事務
33 } 34 //完成依賴事務
35 dependentTransaction.Complete(); 36 } 37 38 static void ShowMessage(string data) 39 { 40 if (Transaction.Current != null) 41 { 42 Transaction transaction = Transaction.Current; 43 string info = string.Format("{0}:{1}\nTransaction:\n DistributedIndentifier:{2} \n LocalIndentifier:{3}\n", 44 data,Thread.CurrentThread.ManagedThreadId.ToString(), 45 transaction.TransactionInformation.DistributedIdentifier, 46 transaction.TransactionInformation.LocalIdentifier); 47 Console.WriteLine(info); 48 } 49 } 50 }

首 先在主線程中利用 Transaction.DependentClone(DependentCloneOption.BlockCommitUntilComplete) 方法生成一個依賴事務,注意方法使用了BlockCommitUntilComplete的方式生成,即事務將在所有依賴事務使用Complete()后 才能執行提交。

然后利用ThreadPool.QueueUserWorkItem(WaitCallback,Object)方法把依賴事務作為回調參數傳遞到回調函數中。

最后在回調函數中使用TransactionScope(transaction)構造函數生成對象,這代表把參數transaction作為當前的環境事務對象。觀察下面的運行結果,兩個線程中的事務都是同一個事務。

結束語

事務是在多個層次都會使用到的,但很多項目當中往往會忽略了這一點而只在數據層使用,在大型的系統當中這樣可能會影響到系統的一致性。特別是在分布式系統當中,操作往往同時存在於多個不同的系統當中,事務的處理更顯示出其重要性。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM