閱讀714 返回首頁    go 阿裏雲 go 技術社區[雲棲]


WCF技術剖析之三十一: WCF事務編程[下篇]

在WCF事務編程模型下,通過服務契約確定事務流轉的策略(參閱《上篇》),通過事務綁定實施事務的流轉(參閱《中篇》)。但是,對於事務綁定接收到並成功創建的事務來說,服務操作的執行是否需要自動登記到該事務之中,以及服務操作采用怎樣的提交方式,這就是服務端自己說了算了。正因為如此,WCF通過服務(操作)行為的形式定義事務的登記和提交(完成)方式。

一、事務的自動登記(Enlistment)與提交(完成)

OperationBehaviorAttribute特性(其本身是一個操作行為)中定了兩個與事務管理相關的屬性:TransactionAutoCompleteTransactionScopeRequired

   1: [AttributeUsage(AttributeTargets.Method)]
   2: public sealed class OperationBehaviorAttribute : Attribute, IOperationBehavior
   3: {
   4:     //其他成員
   5:     public bool TransactionScopeRequired { get; set; }
   6:     public bool TransactionAutoComplete { get; set; }
   7: }

如上麵的代碼所示,這兩個屬性均為布爾類型,它們代表的含義如下:

  • TransactionScopeRequired:表示相應的操作的整個執行是否自動登記到一個事務中。具體來講,如果客戶端流程成功地流入服務端,並被服務端事務綁定成功接收,將該屬性設為True以為著整個操作的執行將自動被納入到流入的事務之中,服務操作將會成為客戶端事務的一部分。如果服務端不曾成功接收流入的事務,將該屬性設為True意味著操作的執行將會被納入到一個新創建的事務中。TransactionScopeRequired的默認值為False;
  • TransactionAutoComplete:表示如果操作執行過程中沒有拋出異常,完成後將自動提交事務(對於最外層的事務)或者向被依賴的事情進行投票(對於嵌套的依賴事務)。TransactionAutoComplete的默認值為True。

如果我們需要將整個操作(而不是操作的一部分)納入到事務中執行,我們隻需要將OperationBehaviorAttribute特性應用到服務類型中的相應的方法之上,並將TransactionScopeRequired屬性設為True即可。相麵的代碼中,我通過應用OperationBehaviorAttribute特性將服務BankingService的Transfer方法定義成事務型操作方法。

   1: public class BankingService : IBankingService
   2: {
   3:     [OperationBehavior(TransactionScopeRequired = true)]
   4:     public void Transfer(string accountFrom, string accountTo, double amount)
   5:     {
   6:          //省略實現
   7:     }
   8: }

將TransactionAutoComplete屬性設為True(默認就是True)可以使我們需要考慮對本地事務的提交問題。隻要執行完最後一句代碼尚無異常拋出,則會提交(或完成)事務。但是有時候,我們需要不同的事務提交(完成)策略,比如服務方法中包含一些非事務型操作(比如日記記錄),隻要保證正常的業務邏輯正常執行就可以提交(完成)事務。在這種情況下,我們可以將該屬性設為False,通過調用當前OperationContext的SetTransactionComplete方法即可實現對本地事務的提交。

   1: public sealed class OperationContext : IExtensibleObject<OperationContext>
   2: {
   3:     //其他成員
   4:     public void SetTransactionComplete();
   5: }

除了定義在OperationBehaviorAttribute特性中的基於操作的行為,還有一些與事務相關的服務行為,它們定義在我們熟悉的ServiceBehaviorAttribute特性中。

二、事務相關的服務行為

如下麵的代碼所示, ServiceBehaviorAttribute特性定義了四個與事務相關的屬性。其中TransactionIsolationLevel指定事務的隔離級別,默認值為IsolationLevel.Serializable;TransactionTimeout以字符串定義事務的超市時限,WCF運行時會根據指定的字符串創建TimeSpan對象;TransactionAutoCompleteOnSessionClose表示在會話正常結束(沒有出現異常)之後是否自動提交或為完成開啟的事務,默認值為False;ReleaseServiceInstanceOnTransactionComplete則表示當事務完成之後是否需要將服務實例釋放掉,默認為False。

   1: [AttributeUsage(AttributeTargets.Class)]
   2: public sealed class ServiceBehaviorAttribute : Attribute, IServiceBehavior
   3: {
   4:     //其他成員
   5:     public IsolationLevel TransactionIsolationLevel { get; set; }
   6:     public string TransactionTimeout { get; set; }
   7:     public bool TransactionAutoCompleteOnSessionClose { get; set; }
   8:     public bool ReleaseServiceInstanceOnTransactionComplete { get; set; }
   9: }

在下麵的代碼中,通過在BankingService上應用ServiceBehaviorAttribute特性將隔離級別設置成ReadCommitted,超時時限設置成5分鍾,並將TransactionAutoCompleteOnSessionClose設置成True。

   1: [ServiceBehavior(TransactionIsolationLevel = IsolationLevel.ReadCommitted,
   2:                  TransactionTimeout = "00:05:00",
   3:                  TransactionAutoCompleteOnSessionClose = true)]
   4: public class BankingService : IBankingService
   5: {
   6:     //省略成員
   7: }

當你通過ServiceBehaviorAttribute特性對上述的四個屬性的任一個進行的設置,即使是設置成默認值,如果服務中並不存在一個TransactionScopeRequired屬性為True的操作,在進行服務寄宿的時候將會拋出異常。就以上麵的設置為例,在BankingService中的唯一的Transfer方法上,並沒有通過OperationBehaviorAttribute將TransactionScopeRequired屬性為True,在對服務進行寄宿的時候,就會拋出如圖1所示的InvalidOperationException異常。

   1: [ServiceBehavior(TransactionIsolationLevel = IsolationLevel.ReadCommitted,
   2:                  TransactionTimeout = "00:05:00",
   3:                  TransactionAutoCompleteOnSessionClose = true)]
   4: public class BankingService : IBankingService
   5: {
   6:     public void Transfer(string accountFrom, string accountTo, double amount)
   7:     {
   8:         //省略實現
   9:     }
  10: }

image 圖1 為不存TransactionScopeRequired操作的服設置在事務相關服務行為導致的異常

通過TransactionTimeout設置的事務超時時限最終會被賦予ChannelDispatcher的同名屬性。該屬性的默認值為TimeSpan.Zero,在這種情況下,運行時將會采用System.Transactions的默認超時設置。如果設置的TransactionTimeout的值超過了System.Transactions設置的最大超時時限,後者將會自動作為運行時的事務超時時限。相關類型請參考《談談分布式事務之三: System.Transactions事務詳解[上篇]》和《談談分布式事務之三: System.Transactions事務詳解[下篇]

   1: public class ChannelDispatcher : ChannelDispatcherBase
   2: {
   3:     //其他成員
   4:     public TimeSpan TransactionTimeout { get; set; }
   5: }

基於事務超時時限的服務行為也可以通過配置的方式指定,對應的配置節為<serviceTimeouts>,你隻需按照所需的各式設置transactionTimeout屬性值即可。在下麵的配置中,我們將BankingService服務的事務超時時限設置成30分鍾。

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <configuration>
   3:     <system.serviceModel>    
   4:         <services>
   5:             <service behaviorConfiguration="transactionBehavior" name="Artech.TransactionalServices.BankingService">
   6:                 <endpoint address="net.tcp://127.0.0.1:3721/bankingservice" binding="netTcpBinding"                  contract="Artech.TransactionalServices.IBankingService" />
   7:             </service>
   8:         </services>
   9:       <behaviors>
  10:         <serviceBehaviors>
  11:           <behavior name="transactionBehavior">
  12:             <serviceTimeouts transactionTimeout="00:30:00" />
  13:           </behavior>
  14:         </serviceBehaviors>
  15:       </behaviors>
  16:     </system.serviceModel>
  17: </configuration>

在事務流轉的場景中,流入的事務和目標服務的事務隔離級別必須一致。也就是說,如果服務調用存在於客戶端現有的事務之中,當前客戶端事務的隔離級別必須和目標服務具有相同的隔離級別。否則,服務端將會返回相應的Fualt消息並導致客戶端拋出異常。同樣對於上麵我們定義的BankingService(TransactionIsolationLevel = IsolationLevel.ReadCommitted),如果客戶端按照下麵的方式進行調用,由於客戶端事務采用默認的隔離界別Serializable,會拋出如圖2所示的ProtocolException異常。

   1: using (ChannelFactory<IBankingService> channelFactory = new ChannelFactory<IBankingService>("bankingservice"))
   2: {
   3:     IBankingService bankingservice = channelFactory.CreateChannel();
   4:     using (TransactionScope transactionScope = new TransactionScope())
   5:     {
   6:         bankingservice.Transfer("Foo", "Bar", 1000);
   7:         transactionScope.Complete();
   8:     }
   9: }

image 圖2 事務隔離級別不一致導致的異常

到此為止,WCF事務編程模型涉及到的三個方麵,即服務(操作)契約、綁定和服務(操作)行為就介紹完了。接下來,我們將給出一個完整的例子。


作者:蔣金楠
微信公眾賬號:大內老A
微博:www.weibo.com/artech
如果你想及時得到個人撰寫文章以及著作的消息推送,或者想看看個人推薦的技術資料,可以掃描左邊二維碼(或者長按識別二維碼)關注個人公眾號(原來公眾帳號蔣金楠的自媒體將會停用)。
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁麵明顯位置給出原文連接,否則保留追究法律責任的權利。
原文鏈接

最後更新:2017-10-27 16:04:31

  上一篇:go  WCF技術剖析之三十一: WCF事務編程[中篇]
  下一篇:go  WCF技術剖析之三十二:一步步創建一個完整的分布式事務應用