<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      C#綜合揭秘——細說事務(wù)

      引言

      其實事務(wù)在數(shù)據(jù)層、服務(wù)層、業(yè)務(wù)邏輯層多處地方都會使用到,在本篇文章將會為大家一一細說。

      其中前面四節(jié)是事務(wù)的基礎(chǔ),后面的三節(jié)是事務(wù)的重點,對事務(wù)有基礎(chǔ)的朋友可以跳過前面四節(jié)。

      文章有錯漏的地方歡迎各位點評。

       

      目錄

      一、事務(wù)的定義

      二、事務(wù)管理器

      三、在ADO.NET中實現(xiàn)事務(wù)

      四、隱式事務(wù) TransactionScope

      五、在WCF中實現(xiàn)事務(wù)

      六、嵌套式事務(wù)

      七、異步事務(wù)

       

       

       

       

      一、事務(wù)的定義

      所謂事務(wù),它是一個操作集合,這些操作要么都執(zhí)行,要么都不執(zhí)行,它是一個不可分割的工作單位。典型的例子就像從網(wǎng)上銀行系統(tǒng)的帳戶A轉(zhuǎn)帳到帳戶B,它經(jīng)過兩個階段:1.從帳戶A取出款項。2.把款項放入帳戶B中。這兩個過程要么同時成功,要么同時失敗,這一系列的操作就被稱為事務(wù)性(Transactional)操作。

      在一個事務(wù)性操作的環(huán)境下,操作有著以下的4種特性,被稱為ACID特性

      原子性(Atomicity) 當事務(wù)結(jié)束,它對所有資源狀態(tài)的改變都被視為一個操作,這些操作要不同時成功,要不同時失敗  
      一致性(Consistency) 操作完成后,所有數(shù)據(jù)必須符合業(yè)務(wù)規(guī)則,否則事務(wù)必須中止
      隔離性(Isolation) 事務(wù)以相互隔離的方式執(zhí)行,事務(wù)以外的實體無法知道事務(wù)過程中的中間狀態(tài)
      持久性(Durable) 事務(wù)提交后,數(shù)據(jù)必須以一種持久性方式存儲起來

       

      回到目錄

       

      二、事務(wù)管理器

      在軟件系統(tǒng)當中可以看到無論在數(shù)據(jù)庫、Web服務(wù)、WCF、文件系統(tǒng)都存在著數(shù)據(jù)參與到事務(wù)運作當中,我們把管理這些數(shù)據(jù)的工具稱為資源管理器RM(Resources Manager)。而事務(wù)管理器TM(Transaction Manager)就是協(xié)調(diào)多個資源管理器的工作,保證數(shù)據(jù)完整性的工具。

       

       

      由上圖可以看到事務(wù)的管理流程,系統(tǒng)通知事務(wù)管理器TM來啟動事務(wù),事務(wù)管理器TM控制向多個資源管理器RM并協(xié)調(diào)RM之間的事務(wù)操作。圖中存在兩個持久化RM,分別管理數(shù)據(jù)庫和文件系統(tǒng),這些事務(wù)操作要不同時成功,要不同時失敗。

       

      事務(wù)管理器一般分為三類:輕量級事務(wù)管理器(LTM)、核心事務(wù)管理器(KTM)、分布式事務(wù)協(xié)調(diào)器(DTC)

       

      1. 輕量級事務(wù)管理器 (LTM)

      它是包括在System.Transactions 命名空間內(nèi)的一個事務(wù)管理框架,它只能管理單個應(yīng)用程序域內(nèi)的事務(wù)。LTM 可以管理多個易變的RM,但只能管理一個持久化RM。若事務(wù)試圖加入第二個持久化RM,那輕量級事務(wù)管理器LTM將提升級別。LTM是性能最高的事務(wù)管理器,在可選擇的情況下應(yīng)該盡可能地使用 LTM 事務(wù)管理器。

      這里易變RM是指它參與會引發(fā) “未確定狀態(tài)” 的2PC事務(wù)時候,不需要恢復服務(wù),更多時候,易變RM的數(shù)據(jù)只存儲在內(nèi)存當中。

      而持久化RM是指它參與會引發(fā) “未確定狀態(tài)” 的2PC事務(wù)時候,它需要恢復服務(wù),持久化RM管理的數(shù)據(jù)是在于硬盤當中。所以,參與2PC事務(wù)的的持久RM必須有新舊兩個版本,如果事務(wù)引發(fā) “未確定狀態(tài)” 的時候,那么它就會聯(lián)系持久化RM,恢復到其中一個版本。

      2PC 是2 Phase Commit的縮寫,代表事務(wù)的2階段提交驗證算法:在數(shù)據(jù)提交時,第一階段:應(yīng)用程序記錄每個數(shù)據(jù)源并執(zhí)行更新請求,TM通知每個RM來執(zhí)行分布式事 務(wù),然后每個RM都對數(shù)據(jù)執(zhí)行本地的事務(wù),在事務(wù)將提交前,TM會與各個RM進行信息交換,以獲知更新是否成功。第二階段,如果其中任何一個RM表示更新 失敗,TM就會通知所有的RM事務(wù)操作失敗,實現(xiàn)數(shù)據(jù)回滾。如果所有RM的操作都成功,那么整個TM事務(wù)就宣告成功。

       

      2. 核心事務(wù)管理器 (KTM)

      KTM是用于Windows Vista和Windows Server 2008 系統(tǒng)中的輕量級事務(wù)管理器,與LTM相像,它可以管理多個易變的RM,但只能管理一個持久化RM。

       

      3. 分布式事務(wù)協(xié)調(diào)器(DTC)

      分布式事務(wù)協(xié)調(diào)器DTC(Distributed Transaction Coordinator)能管理多個持久化RM中的事務(wù),事務(wù)可以跨越應(yīng)用程序域、進程、硬件、域等所有的邊界。在Windows Server 2008當中,DTC支持OleDB、XA、WS-AtomicTransaction、WSCoordination、WS-BusinessActivity等多個協(xié)議。由于分布式事務(wù)需要在多個參與方之間實現(xiàn)多次通訊,所以是一種巨大的開銷,因此,在可以使用LTM和KTM的時候,應(yīng)該盡量避免使用DTC。在上面圖片中的事務(wù)同時啟動了兩個RM分別處理數(shù)據(jù)庫數(shù)據(jù)與文件數(shù)據(jù),當中啟動的就是DTC分布式事務(wù)。

       

      4.事務(wù)類 System.Transactioins.Transaction

      Transaction是由Framework 2.0 就開始引入,用于顯示管理事務(wù)的一個類。通過Transaction可以直接管理事務(wù)的中止、釋放,也可以獲取、克隆當前的環(huán)境事務(wù)類。

      • Transaction的公用屬性

      其中Transaction.Current 比較常用,它可以指向一個當前運行環(huán)境中的事務(wù),如果環(huán)境事務(wù)不存在,系統(tǒng)將返回一個null

      Transaction transaction=Transaction.Current;

      屬性說明
      Current 獲取或設(shè)置環(huán)境事務(wù)。
      IsolationLevel 獲取事務(wù)的隔離級別。
      TransactionInformation 檢索有關(guān)某個事務(wù)的附加信息。
      • Transaction的常用公用方法

      其中Rollback、Dispose方法可以控制事務(wù)中止、釋放,而Clone、DependentClone方法在多線程操作中經(jīng)常用到,在 “異步事務(wù)” 一節(jié)中將詳細說明

      方法說明
      Rollback 中止事務(wù)、回滾。
      Dispose 釋放事務(wù)對象。
      Clone     創(chuàng)建事務(wù)克隆
      DependentClone 創(chuàng)建事務(wù)的依賴克隆。
      • Transaction的事件

      在事務(wù)完成后,會觸發(fā)TransactionCompleted事件,開發(fā)人員可以在此事件的過程監(jiān)測其狀態(tài)

      事件說明
      TransactionCompleted 在事務(wù)完成后執(zhí)行

      5. 事務(wù)狀態(tài) TransactionInformation

      上面講解過事務(wù)分為本地事務(wù)與分布式事務(wù),而Transaction類的TransactionInformation是事務(wù)狀態(tài)的記錄,它可以跟蹤事務(wù)動作,分辨事務(wù)現(xiàn)處的狀態(tài),記錄本地事務(wù)與分布式事務(wù)的Guid。

      TransactionInformation有兩個重要成員

       1 public class TransactionInformation
      2 {
      3 //返回分布式事務(wù)標識符
      4 public Guid DistributedIdentifier
      5 {get;}
      6
      7 //返回本地事務(wù)標識符
      8 public string LocalIdentifier
      9 {get;}
      10 }

      LocalIndentifier是本地事務(wù)的標識符,它可以獲取本地事務(wù)管理器(LTM)的ID,并且注意只要事務(wù)存在,它的值就永遠不會是null。它包含兩個部分,一個是LTM的Guid,它是應(yīng)用程序中的唯一值,代表了現(xiàn)存應(yīng)用程序域分配的LTM。另一部分是一個可變量,代表了當時該應(yīng)用程序域中的事務(wù)數(shù)量。

      例如:3427dec9-4abc-34cc-9edf-30ad835c33k3:3

      其中3427dec9-4abc-34cc-9edf-30ad835c33k3是此本地事務(wù)管理器的Guid,在事務(wù)啟動后,此值都是不變的,而 “3” 代表此刻該應(yīng)用程序域中存在 “3” 個本地事務(wù)。


      DistributedIndentifier是分布式事務(wù)的標識符,在普通情況下DistributedIndentifier的值都為Guid.Empty。但當LTM或KTM事務(wù)被提升到分布式事務(wù)時,DistributedIndentifier就會產(chǎn)生。最重的是,在同一個分布式事務(wù)管理器當中,即使事務(wù)跨越服務(wù)邊界,分布式ID都是一致的。DistributedIndentifier是分布式事務(wù)的唯一標識符,它的使用方法在后面 “事務(wù)的傳播” 一節(jié)將詳細介紹。

      在TransactionManager類中,還提供了一個事件DistributedTransactionStarted專門用于測試分布式事務(wù)的變化。

       1     class Program
      2 {
      3 static void Main(string[] args)
      4 {
      5 using (TransactionScope scope = new TransactionScope())
      6 {
      7 TransactionManager.DistributedTransactionStarted += OnDistributedTransactionStarted;
      8 ............
      9 scope.Complete();
      10 }
      11 Console.ReadKey();
      12 }
      13
      14 //當執(zhí)行分布式事務(wù)是就會啟動此方法顯示事務(wù)信息
      15 static void OnDistributedTransactionStarted(object sender, TransactionEventArgs args)
      16 {
      17 Transaction transaction = args.Transaction;
      18 Console.WriteLine("Distributed Transaction Started!\n DistributedIndentifier:"
      19 +transaction.TransactionInformation.DistributedIdentifier);
      20 }
      21 }

      基礎(chǔ)知識就先講到這里,下面開始介紹一下事務(wù)的具體用法。

       

      回到目錄

       

      三、在ADO.NET中實現(xiàn)事務(wù)

      1. ADO.NET事務(wù)的主要成員

      需要使用事務(wù)管理的情況很多,在數(shù)據(jù)層使用得特別廣泛,幾乎每一個系統(tǒng)的數(shù)據(jù)層都會實現(xiàn)事務(wù)。數(shù)據(jù)層的事務(wù)都是繼承自DBTransaction,派生自IDbTransaction的。下面介紹一下IDbTransaction的基本成員:

      1 public interface IDbTransaction:IDisposable
      2 {
      3 IDbConnection Connection {get;} //返回Connection對象
      4 IsolationLevel IsolationLevel{get;}
      5 void Commit(); //數(shù)據(jù)提交,把所有改變數(shù)據(jù)保存到持久化數(shù)據(jù)庫
      6 void Rollback(); //數(shù)據(jù)回滾,把所有數(shù)據(jù)恢復原值
      7 }

      其中Connection屬性是返回初始化此事務(wù)時所引用的連接對象的。Commit()方法應(yīng)該在完成所有數(shù)據(jù)操作后才調(diào)用,調(diào)用該方法后,已經(jīng)改變的數(shù)據(jù)將會保存到持久化數(shù)據(jù)庫當中。而Rollback()是出現(xiàn)錯誤時調(diào)用的,調(diào)用后數(shù)據(jù)將返回初始值。IsolationLevel是指定遇到其它并行事務(wù)時的處理方式。

      ADO.NET當中有多個子類都繼續(xù)自DBTransaction,其中SqlTransaction是比較常用的,SqlTransaction中還定義了一個Save()方法,這個方法允許開發(fā)人員把失敗的事務(wù)回滾到上一個保存點而不回滾整個事務(wù)。而在DataContext類里面,Transaction屬性會返回DBTransaction對象。

       

       

      2.開發(fā)實例

      在傳統(tǒng)的ADO.NET中使用事務(wù),方法如下:

       1 private static void Execute(string connectionString)
      2 {
      3 using (SqlConnection connection = new SqlConnection(connectionString))
      4 {
      5 connection.Open();
      6
      7 SqlCommand command = connection.CreateCommand();
      8 SqlTransaction transaction;
      9
      10 //啟動事務(wù)
      11 transaction = connection.BeginTransaction("SampleTransaction");
      12
      13 //設(shè)定SqlCommand的事務(wù)和連接對象
      14 command.Connection = connection;
      15 command.Transaction = transaction;
      16
      17 try
      18 {
      19 command.CommandText ="Insert into ......";
      20 command.ExecuteNonQuery();
      21
      22 // 完成提交
      23 transaction.Commit();
      24 ......
      25 }
      26 catch (Exception ex)
      27 {
      28 //數(shù)據(jù)回滾
      29 transaction.Rollback();
      30 .....
      31 }
      32 }
      33 }

      在DataContext中使用事務(wù),方法極其相似,不同的是SqlCommand中事務(wù)為SqlTransaction,在DataContext中事務(wù)為DbTransaction

       1  using(MyDataContext context=new MyDataContext())
      2 {
      3 try
      4 {
      5 context.Connection.Open();
      6 context.Transaction=context.Connection.BeginTransaction();
      7 //更新數(shù)據(jù)
      8 .........
      9 context.SubmitChanges();
      10 //事務(wù)提交
      11 context.Transaction.Commit();
      12 }
      13 catch(Excetion ex)
      14 {
      15 //數(shù)據(jù)回滾
      16 context.Transaction.Rollback();
      17 //錯誤處理
      18 .........
      19 }
      20 }

       

      回到目錄

       

      四、隱式事務(wù) TransactionScope

      1. TransactionScope的概念

      TransactionScope存在于System.Transactions 命名空間中, 它是從Framework 2.0開始引入的一個事務(wù)管理類,它也是微軟推薦使用的一個事務(wù)管理類。在TransactionScope的構(gòu)造函數(shù)中會自動創(chuàng)建了一個新的LTM(輕量級事務(wù)管理器),并通過Transaction.Current 隱式把它設(shè)置為環(huán)境事務(wù)。在使用隱式事務(wù)時,事務(wù)完成前程序應(yīng)該調(diào)用TransactionScope的Complete()方法,把事務(wù)提交,最后利用Dispose()釋放事務(wù)對象。若執(zhí)行期間出現(xiàn)錯誤,事務(wù)將自動回滾。

       1 public class TransactionScope:IDisposable
      2 {
      3 //多個構(gòu)造函數(shù)
      4 public TransactionScope();
      5 public TransactionScope(Transaction)
      6 public TransactionScope(TransactionScopeOption)
      7 ......
      8 public void Complete(); //提交事務(wù)
      9 public void Dispose(); //釋放事務(wù)對象
      10 }
      11
      12 //調(diào)用方式
      13 using(TransactionScope scope=new TransactionScope())
      14 {
      15 //執(zhí)行事務(wù)型工作
      16 ............
      17 scope.Complete();
      18 }

       

      2. TransactionScope的構(gòu)造函數(shù) TransactionScope(transactionScopeOption)

      TransactionScopeOption 是枚舉的一個實例,它主要用于TransactionScope的構(gòu)造函數(shù)內(nèi),定義事務(wù)生成的狀態(tài)要求。

      在MSDN里面可以找到它的定義:

      http://msdn.microsoft.com/zh-cn/library/system.transactions.transactionscopeoption.aspx

      成員名稱說明
      Required 該范圍需要一個事務(wù)。 如果已經(jīng)存在環(huán)境事務(wù),則使用該環(huán)境事務(wù)。 否則,在進入范圍之前創(chuàng)建新的事務(wù)。 這是默認值。
      RequiresNew 總是為該范圍創(chuàng)建新事務(wù)。
      Suppress 環(huán)境事務(wù)上下文在創(chuàng)建范圍時被取消。 范圍中的所有操作都在無環(huán)境事務(wù)上下文的情況下完成。

       

      這里Suppress有點特別,當使用Suppress范圍內(nèi),所有的操作都將在無事務(wù)的上下文中執(zhí)行,即當中的程序不再受到事務(wù)的保護,這大多數(shù)在嵌套式事務(wù)中使用。

       1 void DoWork()
      2 {
      3 using(TransactionScope scope=new TransactionScope())
      4 {
      5 //在事務(wù)環(huán)境中執(zhí)行操作
      6 ......
      7 NoTransaction();
      8 scope.Complete();
      9 }
      10 }
      11
      12 void NoTransaction()
      13 {
      14 //在無事務(wù)環(huán)境中執(zhí)行操作
      15 using(TransactionScope scope=new TransactionScope(TransactionScopeOption.Suppress))
      16 {
      17 ......
      18 }
      19 }



      3. 應(yīng)用實例,在Entity Framework中使用TransactionScope

      在 ObjectContext.SaveChanges 方法中已經(jīng)包含了事務(wù)的保護,自從微軟開源了Entity Framework 后,在下才在CodePlex 了解到其實現(xiàn)方式。一般在單體個實體的插入、更新、刪除等簡單操作中,不需要額外加載事務(wù)。除非在關(guān)聯(lián)表操作,多次使用ObjectContext.SaveChanges方法時,此時則需要使用事務(wù)保存數(shù)據(jù)操作的一致性。因為在SaveChanges中已經(jīng)包含了事務(wù),此時所實現(xiàn)的事務(wù)操作已經(jīng)第六節(jié)所說到的嵌套式事務(wù)。

       1         public int UpdateOrder(Order order)
       2         {
       3             using (BusinessEntities context = new BusinessEntities())
       4             {
       5                 EntityKey entityKey = new
       6                     EntityKey("BasicArchitectureEntities.Order", "Id", order.Id);
       7                 var objResult = context.GetObjectByKey(entityKey);
       8 
       9                 using (TransactionScope scope = new TransactionScope())
      10                 {
      11                     if (objResult != null)
      12                         context.ApplyCurrentValues<Order>("Order", order);
      13                     foreach (OrderItem item in order.OrderItem)
      14                         UpdateOrderItem(item);
      15                     return context.SaveChanges();
      16                 }
      17             }
      18         }
      19 
      20         public int UpdateOrderItem(OrderItem item)
      21         {
      22             using (BusinessEntities context = new BusinessEntities())
      23             {
      24                 EntityKey entityKey = new
      25                     EntityKey("BasicArchitectureEntities.OrderItem", "Id", item.Id);
      26                 var objResult = context.GetObjectByKey(entityKey);
      27 
      28                 //更新實體屬性
      29                 if (objResult != null)
      30                     context.ApplyCurrentValues<OrderItem>("OrderItem", item);
      31                 return context.SaveChanges();
      32             }
      33         }

       

      回到目錄

       

      五、在WCF中實現(xiàn)事務(wù)

      1. WCF服務(wù)中事務(wù)的邊界

      把WCF的事務(wù)邊界獨立成一節(jié),是想大家注意這一點,WCF服務(wù)中,事務(wù)是以方法為邊界的,每個WCF服務(wù)的方法可以有獨立事務(wù)的執(zhí)行模式。而事務(wù)可以在多個服務(wù)中傳播,也可以在服務(wù)端與客戶端之間傳播,介時事務(wù)管理器的級數(shù)將會晉升。

       

      2.簡單的事務(wù)使用方式

      TransactionScopeRequired與TransactionAutoComplete是WCF事務(wù)的基本元素。

      當TransactionScopeRequired等于true時,代表在此WCF服務(wù)的方法中啟動事務(wù)。反之,當此值為false時代表此方法不執(zhí)行事務(wù)。

      當TransactionAutoComplete等于true時,代表該方法使用隱式事務(wù),這也是微軟推薦使用的方法。即當該方法在運行過程中沒有拋出Exception,操作就默認為完成,事務(wù)將自動提交。如果期間出現(xiàn)任何異常,事務(wù)就會自動回滾。如果TransactionAutoComplete等于false時,該方法即為顯式事務(wù),即需要在方法完成時利用OperationContext.Current.SetTransactionComplete () 顯式提交事務(wù)。

       1 [ServiceContract(SessionMode=SessionMode.Required)]
      2 public interface IService
      3 {
      4 [OperationContract]
      5 void Method1();
      6
      7 [OperationContract]
      8 void Method2();
      9 }
      10
      11 public class Service : IService
      12 {
      13 //隱式事務(wù)
      14 [OperationBehavior(TransactionScopeRequired=true,TransactionAutoComplete=true)]
      15 public void Method1()
      16 {
      17 ...........
      18 }
      19
      20 //顯式事務(wù)
      21 [OperationBehavior(TransactionScopeRequired=true,TransactionAutoComplete=false)]
      22 public void Method2()
      23 {
      24 ...........
      25 OperationContext.Current.SetTransactionComplete();
      26 }
      27 }


      3. 事務(wù)的傳播

      在同一個應(yīng)用程序域中,事務(wù)默認能相互傳播,在上面的例子當中,當方法Method1()直接調(diào)用Mehtod2()的時候,事務(wù)也能夠成功流轉(zhuǎn)。

      事務(wù)也能夠在服務(wù)端與客戶端之間傳播,還能跨越服務(wù)邊界,在多個系統(tǒng)當中流轉(zhuǎn),在WCF里把服務(wù)中的事務(wù)傳播稱作事務(wù)流(Transaction Flow)。如果事務(wù)流需要在服務(wù)端和客戶端成功傳播或使用分布式事務(wù),必須具備以下條件:

      • 綁定必須支持事務(wù),在WCF內(nèi)并非所有綁定都支持事務(wù),像常用的BasicHttpBinding就不支持事務(wù)的傳播。只有以下幾個綁定才能支持事務(wù)流的運轉(zhuǎn):NetTcpBinding、WSHttpBinding、WSDualHttpBinding、WSFederationHttpBinding、NetNamedPipeBinding。
      • 服務(wù)方法必須設(shè)置好TransactionScopeRequired與TransactionAutoComplete兩個事務(wù)的基本元素,要成功啟動事務(wù),這是基礎(chǔ)條件。
      • 把TransactionFlow設(shè)置為true,這代表啟動事務(wù)流,允許在SOAP頭部放置事務(wù)的信息。一般情況下TransactionFlow的默認值為false ,這表示事務(wù)只能在服務(wù)器的同一應(yīng)用程序域內(nèi)流轉(zhuǎn),而不能實現(xiàn)服務(wù)端與客戶端之間的傳播。
      • 把服務(wù)契約的TransactionFlowOption設(shè)置為Allowed,這代表允許客戶端的事務(wù)傳播到服務(wù)端。
      • 客戶端必須啟動一個事務(wù),在最后使用TransactionScope.Complete ( ) 提交事務(wù)。
      TransactionFlowOption說明
      TransactionFlowOption有三個選項:

      一為NotAllowed,這代表了禁止客戶端傳播事務(wù)流到服務(wù)端,即使客戶端啟動了事務(wù),該事務(wù)也會被忽略;
      二為Allowed,這代表允許客戶端的事務(wù)傳播到服務(wù)端,但服務(wù)器端不一定會引用到此事務(wù);
      三為Mandatory,這代表服務(wù)端與客戶端必須同時啟動事務(wù)流,否則就會拋出InvalidOperationException異常。

       

      下面舉幾個例子來講解一下事務(wù)流的使用方式。

       3.1 在服務(wù)端與客戶端之間傳播事務(wù)

      這是事務(wù)流的基本使用方式,首先在服務(wù)端使用wsHttpBinding綁定建立一個服務(wù)契約,在方法中利用TransactionInformation對象檢測一下事務(wù)的狀態(tài)。然后設(shè)置好TransactionScopeRequired與TransactionAutoComplete屬性來啟動事務(wù),在*.config中把TransactionFlow設(shè)置為true。再把服務(wù)的TransactionFlowOption設(shè)置為Allowed,最后在客戶端通過TransactionScope.Complete()的方法提交事務(wù)。

      服務(wù)端:

       1 namespace Example
      2 {
      3 [ServiceContract]
      4 public interface IExampleService
      5 {
      6 [OperationContract]
      7 void Method1();
      8 }
      9
      10 public class ExampleService : IExampleService
      11 {
      12 //使用隱式事務(wù),并把TransactionFlowOption設(shè)置為Allowed打開事務(wù)流
      13 [OperationBehavior(TransactionAutoComplete=true,TransactionScopeRequired=true)]
      14 [TransactionFlow(TransactionFlowOption.Allowed)]
      15 public void Method1()
      16 {
      17 //通過TransactionInformation檢測事務(wù)狀態(tài)
      18 Transaction transaction = Transaction.Current;
      19 string info=string.Format(" DistributedIndentifier:{0} \n LocalIndentifier:{1}",
      20 transaction.TransactionInformation.DistributedIdentifier,
      21 transaction.TransactionInformation.LocalIdentifier);
      22 Console.WriteLine("Method1: \n"+info);
      23 }
      24
      25 static void Main(string[] args)
      26 {
      27 //啟動服務(wù)
      28 ServiceHost exampleHost = new ServiceHost(typeof(Example.ExampleService));
      29
      30 exampleHost.Open();
      31
      32 Console.WriteLine("service start");
      33 Console.ReadKey();
      34 Console.WriteLine("service end");
      35
      36 exampleHost.Close();
      37 }
      38 }
      39 }
      40
      41 <configuration>
      42 <system.serviceModel>
      43 <bindings>
      44 <wsHttpBinding>
      45 <!--啟動事務(wù)流-->
      46 <binding name="defaultWSHttpBinding" transactionFlow="true" />
      47 </wsHttpBinding>
      48 </bindings>
      49 <behaviors>
      50 <serviceBehaviors>
      51 <behavior name="">
      52 <serviceMetadata httpGetEnabled="true" />
      53 <serviceDebug includeExceptionDetailInFaults="false" />
      54 </behavior>
      55 </serviceBehaviors>
      56 </behaviors>
      57 <services>
      58 <service name="Example.ExampleService">
      59 <!--使用支持事務(wù)流的wsHttpBinding綁定-->
      60 <endpoint address="" binding="wsHttpBinding" bindingConfiguration="defaultWSHttpBinding"
      61 contract="Example.IExampleService">
      62 <identity>
      63 <dns value="localhost" />
      64 </identity>
      65 </endpoint>
      66 <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
      67 <host>
      68 <baseAddresses>
      69 <add baseAddress="http://localhost:7200/Example/ExampleService/" />
      70 </baseAddresses>
      71 </host>
      72 </service>
      73 </services>
      74 </system.serviceModel>
      75 </configuration>

      客戶端:

       1  class Program
      2 {
      3 static void Main(string[] args)
      4 {
      5 using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
      6 {
      7 ShowTransactionMessage("start");
      8
      9 ExampleServiceReference.ExampleServiceClient exampleService1 = new
      10 ExampleServiceReference.ExampleServiceClient();
      11 exampleService1.Method1();
      12 ShowTransactionMessage("exampleService started");
      13 exampleService1.Close();
      14 //事務(wù)提交
      15 scope.Complete();
      16 }
      17 Console.ReadKey();
      18 }
      19
      20 //檢查事務(wù)的狀態(tài)
      21 static void ShowTransactionMessage(string data)
      22 {
      23 if (Transaction.Current != null)
      24 {
      25 Transaction transaction = Transaction.Current;
      26 string info = string.Format(" DistributedIndentifier:{0} \n LocalIndentifier:{1}",
      27 transaction.TransactionInformation.DistributedIdentifier,
      28 transaction.TransactionInformation.LocalIdentifier);
      29 Console.WriteLine(data+" \n" + info);
      30 }
      31 }
      32 }

      圖中上面代表服務(wù)端,下面代表客戶端。可以看到,在客戶端剛啟動事務(wù)時,事務(wù)只一般的LTM輕量級事務(wù),DistributedIndentifier為空值。當調(diào)用ExampleService服務(wù)后,事務(wù)的級別便提升為分布式事務(wù)。而服務(wù)端與客戶端的事務(wù)正是通過DistributedIndentifier為標識的。

       


      3.2使用分布式事務(wù)協(xié)調(diào)多個服務(wù)端的操作

      在分布式系統(tǒng)當中,單個客戶端可能引用多個服務(wù),分布式事務(wù)能協(xié)調(diào)多方的操作。多個系統(tǒng)中的操作要不同時成功,要不同時失敗。下面的例子中,客戶端同時引用了ExampleService服務(wù)和ExtensionService服務(wù),并啟動了分布式事務(wù)。而在客戶端與兩個服務(wù)端的事務(wù)都是通過DistributedIndentifier 作為事務(wù)的標識的。

      服務(wù)端:

        1 //*******************************ExampleService*************************************************************************//
      2 namespace Example
      3 {
      4 [ServiceContract]
      5 public interface IExampleService
      6 {
      7 [OperationContract]
      8 void Method1();
      9 }
      10
      11 public class ExampleService : IExampleService
      12 {
      13 //使用隱式事務(wù),并把TransactionFlowOption設(shè)置為Allowed
      14 [OperationBehavior(TransactionAutoComplete=true,TransactionScopeRequired=true)]
      15 [TransactionFlow(TransactionFlowOption.Allowed)]
      16 public void Method1()
      17 {
      18 //通過TransactionInformation檢測事務(wù)狀態(tài)
      19 Transaction transaction = Transaction.Current;
      20 string info=string.Format(" DistributedIndentifier:{0} \n LocalIndentifier:{1}",
      21 transaction.TransactionInformation.DistributedIdentifier,
      22 transaction.TransactionInformation.LocalIdentifier);
      23 Console.WriteLine("Method1: \n"+info);
      24 }
      25
      26 static void Main(string[] args)
      27 {
      28 //啟動服務(wù)
      29 ServiceHost exampleHost = new ServiceHost(typeof(Example.ExampleService));
      30
      31 exampleHost.Open();
      32
      33 Console.WriteLine("service start");
      34 Console.ReadKey();
      35 Console.WriteLine("service end");
      36
      37 exampleHost.Close();
      38 }
      39 }
      40 }
      41
      42 <configuration>
      43 <system.serviceModel>
      44 <bindings>
      45 <wsHttpBinding>
      46 <!--啟動事務(wù)流-->
      47 <binding name="defaultWSHttpBinding" transactionFlow="true" />
      48 </wsHttpBinding>
      49 </bindings>
      50 <behaviors>
      51 <serviceBehaviors>
      52 <behavior name="">
      53 <serviceMetadata httpGetEnabled="true" />
      54 <serviceDebug includeExceptionDetailInFaults="false" />
      55 </behavior>
      56 </serviceBehaviors>
      57 </behaviors>
      58 <services>
      59 <service name="Example.ExampleService">
      60 <!--使用支持事務(wù)流的wsHttpBinding綁定-->
      61 <endpoint address="" binding="wsHttpBinding" contract="Example.IExampleService"
      62 bindingConfiguration="defaultWSHttpBinding" >
      63 <identity>
      64 <dns value="localhost" />
      65 </identity>
      66 </endpoint>
      67 <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
      68 <host>
      69 <baseAddresses>
      70 <add baseAddress="http://localhost:7200/Example/ExampleService/" />
      71 </baseAddresses>
      72 </host>
      73 </service>
      74 </services>
      75 </system.serviceModel>
      76 </configuration>
      77
      78 //*************************************Extension**************************************************************//
      79 namespace Extension
      80 {
      81 [ServiceContract]
      82 public interface IExtensionService
      83 {
      84 [OperationContract]
      85 void DoWork();
      86 }
      87
      88 public class ExtensionService : IExtensionService
      89 {
      90 [OperationBehavior(TransactionAutoComplete=true,TransactionScopeRequired=true)]
      91 [TransactionFlow(TransactionFlowOption.Allowed)]
      92 public void DoWork()
      93 {
      94 Transaction transaction = Transaction.Current;
      95 string info = string.Format(" DistributedIndentifier:{0} \n LocalIndentifier:{1}",
      96 transaction.TransactionInformation.DistributedIdentifier,
      97 transaction.TransactionInformation.LocalIdentifier);
      98 Console.WriteLine("DoWork: \n" + info);
      99 }
      100
      101 static void Main(string[] args)
      102 {
      103 Console.WriteLine("extension service start");
      104 ServiceHost host = new ServiceHost(typeof(ExtensionService));
      105 host.Open();
      106 Console.ReadKey();
      107 host.Close();
      108 }
      109 }
      110 }
      111 <configuration>
      112 <!--略-->
      113 ...................
      114 </configuration>

      客戶端

       1 namespace Test
      2 {
      3 class Program
      4 {
      5 static void Main(string[] args)
      6 {
      7 using (TransactionScope scope = new TransactionScope(TransactionScopeOption.RequiresNew))
      8 {
      9 ShowTransactionMessage("start");
      10
      11 ExampleServiceReference.ExampleServiceClient exampleService1 = new
      12 ExampleServiceReference.ExampleServiceClient();
      13 exampleService1.Method1();
      14 ShowTransactionMessage("exampleService started");
      15 ExtensionServiceReference.ExtensionServiceClient extensionService = new
      16 ExtensionServiceReference.ExtensionServiceClient();
      17 extensionService.DoWork();
      18 ShowTransactionMessage("extensionService started");
      19
      20 exampleService1.Close();
      21 extensionService.Close();
      22
      23 scope.Complete();
      24 }
      25
      26 Console.ReadKey();
      27 }
      28
      29 //檢查事務(wù)的狀態(tài)
      30 static void ShowTransactionMessage(string data)
      31 {
      32 if (Transaction.Current != null)
      33 {
      34 Transaction transaction = Transaction.Current;
      35 string info = string.Format(" DistributedIndentifier:{0} \n LocalIndentifier:{1}",
      36 transaction.TransactionInformation.DistributedIdentifier,
      37 transaction.TransactionInformation.LocalIdentifier);
      38 Console.WriteLine(data+" \n" + info);
      39 }
      40
      41 }
      42 }
      43 }

      從測試結(jié)果可以看到在多個不同的服務(wù)端與客戶端之間,都是通過DistributedIndentifier分布式事務(wù)ID來進行信息溝通的。一旦任何一出現(xiàn)問題,事務(wù)都會共同回滾,這對分布式開發(fā)是十分重要的。

      值得注意的一點,事務(wù)必須由客戶端提交,當客戶端調(diào)用無事務(wù)狀態(tài)時,兩個服務(wù)端的事務(wù)則無法進行辨認,即其中一方出錯,事務(wù)出現(xiàn)回滾,另一方也無法感知,這樣容易引起邏輯性錯誤。試著把客戶端代碼改為 using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Suppress)){...},再運作一下,可以看到以下結(jié)果。事務(wù)都是由兩個服務(wù)端分別管理,系統(tǒng)并無啟動分布式事務(wù)。

      應(yīng)該注意:分布式事務(wù)會耗費較大的資源,在沒必要的情況下,應(yīng)該盡量使用LTM級量級事務(wù)管理器,而避免使用DTC分布式事務(wù)管理。

       

      4.事務(wù)的的隔離性

      事務(wù)的隔離性是通過TransactionIsolationLevel來定義的,它存在以下幾個級別:

      Unspecified    
      ReadUncommitted 在讀取數(shù)據(jù)時保持共享鎖以避免讀取已修改的數(shù)據(jù),但在事務(wù)結(jié)束前這些數(shù)據(jù)可能已更改,因此會導致不可重復的讀取和虛假數(shù)據(jù)。
      ReadCommitted 發(fā)出共享鎖定并允許非獨占方式的鎖定。
      RepeatableRead 在查詢中使用的所有數(shù)據(jù)上放置鎖,以防止其他用戶更新這些數(shù)據(jù)。這防止了不可重復的讀取,但仍有可能產(chǎn)生虛假行。
      Serializable 默認級別,也是最高級別。表示事務(wù)完成前禁止外界更新數(shù)據(jù)
      Chaos 不使用隔離  
      Snapshot  

       

      需要注意服務(wù)端與客戶端必須使用同一級別的隔離模式,否則系統(tǒng)將會拋出FaultException異常。

      服務(wù)類必須在至少一個方法開啟了事務(wù)后才可以設(shè)置隔離模式

       1    public interface IService
      2 {
      3 [OperationContract]
      4 void Method();
      5 }
      6
      7 [ServiceBehavior(TransasctionIsolationLevel=IsolationLevel.ReadCommitted)]
      8 public class Service : IService
      9 {
      10 //隱式事務(wù)
      11 [OperationBehavior(TransactionScopeRequired=true,TransactionAutoComplete=true)]
      12 public void Method()
      13 {..........}
      14 }

       

       

      5.事務(wù)的超時

      當事務(wù)實現(xiàn)隔離時,資源將會被鎖定,如果一些事務(wù)長期占有資源,那將容易造成死鎖,為了避免這個問題,事務(wù)有一個超時限制,這個超時默認值為60s。如果事務(wù)超過此時間,即使沒有發(fā)生異常,也會自動中止。

      超時時候可以通過特性設(shè)置,也可使用*.config文件設(shè)置。下面的兩段代碼有著相同的效果,就是把超時時間設(shè)置為10s。

       1 [ServiceBehavior(TransactionTimeout="00:00:10")]
      2 public class Service:IService
      3 {......}
      4
      5 <configuration>
      6 ........
      7 <system.serviceModel>
      8 ........
      9 <services>
      10 <service name="MyService" behaviorConfiguration="myBehavior">
      11 ......
      12 </service>
      13 </services>
      14 <behaviors>
      15 <serviceBehaviors>
      16 <behavior name="myBehavior" transactionTimeout="00:00:10"/>
      17 </serviceBehaviors>
      18 </behaviors>
      19 </system.serviceModel>
      20 </configuration>

       

      回到目錄

       

      六、嵌套式事務(wù)

      嵌套式事務(wù)經(jīng)常會出現(xiàn)在項目中,但往往容易被大家忽略,下面介紹一下 嵌套式事務(wù)的用法:

       1 using (TransactionScope scope1 = new TransactionScope())            
      2 {
      3 ..............
      4 using (TransactionScope scope2=new TransactionScope(TransactionScopeOption.RequiresNew))
      5 {
      6 ..............
      7 scope2.Complete(); //只完成嵌套式的內(nèi)部事務(wù),但事務(wù)并未正式提交
      8 }
      9 scope1.Complete(); //代表完成所有事務(wù),事務(wù)正式提交
      10 }

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

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

       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 }

       

      回到目錄

       

      七、異步事務(wù)

      記得在第二節(jié)的時候曾經(jīng)提起過事務(wù)類Transaction的方法中包含方法

      public DependentTransaction DependentClone(DependentCloneOption)

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

      一為BlockCommitUntilComplete,這表示在依賴事務(wù)未完成前,事務(wù)將處于阻塞狀態(tài),只有在所有依賴事務(wù)完成后,事務(wù)才能執(zhí)行提交;

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

       

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

       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 //獲取一個依賴事務(wù),把依賴事務(wù)作為回調(diào)參數(shù)傳到回調(diào)函數(shù)中
      16 DependentTransaction dependentTransaction=
      17 Transaction.Current.DependentClone(DependentCloneOption.BlockCommitUntilComplete);
      18 ThreadPool.QueueUserWorkItem(new WaitCallback(AsyncThread), dependentTransaction);
      19 ........
      20 scope.Complete(); //完成主線程事務(wù),在依賴事務(wù)完成前,事務(wù)提交將處于阻塞狀態(tài)
      21 }
      22 }
      23
      24 static void AsyncThread(object transaction)
      25 {
      26 //獲取依賴事務(wù),利用TransactionScope(Transaction)構(gòu)造函數(shù)生成隱式事務(wù)
      27 DependentTransaction dependentTransaction = (DependentTransaction)transaction;
      28 using (TransactionScope scope = new TransactionScope(dependentTransaction))
      29 {
      30 ShowMessage("AsyncThread");
      31 ..........
      32 scope.Complete(); //完成異步事務(wù)
      33 }
      34 //完成依賴事務(wù)
      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) 方法生成一個依賴事務(wù),注意方法使用了BlockCommitUntilComplete的方式生成,即事務(wù)將在所有依賴事務(wù)使用Complete()后才能執(zhí)行提交。

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

      最后在回調(diào)函數(shù)中使用TransactionScope(transaction)構(gòu)造函數(shù)生成對象,這代表把參數(shù)transaction作為當前的環(huán)境事務(wù)對象。觀察下面的運行結(jié)果,兩個線程中的事務(wù)都是同一個事務(wù)。

       

      結(jié)束語

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

      希望這篇文章能對大家的工作有幫助,對 .NET 開發(fā)有興趣的朋友歡迎加入QQ群:162338858 同探討 !

      回到目錄

      C#綜合揭秘

      通過修改注冊表建立Windows自定義協(xié)議
      Entity Framework 并發(fā)處理詳解

      細說進程、應(yīng)用程序域與上下文

      細說多線程(上)

      細說多線程(下)
      細說事務(wù)
      深入分析委托與事件

       

      作者:風塵浪子
      http://www.rzrgm.cn/leslies2/archive/2012/01/05/2289106.html

      原創(chuàng)作品,轉(zhuǎn)載時請注明作者及出處





      posted on 2012-01-05 10:23  風塵浪子  閱讀(39007)  評論(51)    收藏  舉報

      導航

      主站蜘蛛池模板: 亚洲中文字幕有综合久久| 国产一区二区三区我不卡| 国产精品无码免费播放| 国产精品久久久久鬼色| 亚洲人成网7777777国产| 精品日韩精品国产另类专区 | 免费无码黄十八禁网站| 日韩最新中文字幕| 国产一区二区三区乱码在线观看| 尤物yw193无码点击进入| 色综合色综合色综合久久| 免费无码一区无码东京热| 亚洲成人av综合一区| 猫咪AV成人永久网站在线观看 | 国产精品成人一区二区不卡| 精品国产成人亚洲午夜福利| 丰满少妇高潮无套内谢| 国产精品午夜剧场免费观看| 国模雨珍浓密毛大尺度150p| 好吊视频一区二区三区人妖| 亚洲熟妇色xxxxx欧美老妇| 施秉县| a在线观看视频在线播放| 午夜福利片1000无码免费| 深夜国产成人福利在线观看| 亚洲国产激情一区二区三区| 婷婷色综合成人成人网小说| 精品午夜福利短视频一区| 狠狠亚洲色一日本高清色| 亚洲精品漫画一二三区| 精品午夜福利无人区乱码| 国产成人一区二区三区在线| 亚洲 欧美 唯美 国产 伦 综合| 琼中| 欧美国产日产一区二区| 久久这里都是精品二| chinese极品人妻videos| 中文字幕日本一区二区在线观看 | 最新国内精品自在自线视频| 亚洲欧美日韩在线码| 国产黄色三级三级看三级|