WCF把書讀薄(3)——數據契約、消息契約與錯誤契約html
真不愧是老A的書,例子多,並且也講了很多原理方面的內容,不過越讀越以爲壓力山大……此次來稍微整理整理事務和可靠會話的內容。數據庫
十8、事務編程編程
WCF的分佈式事務編程,指的是在客戶端定義一個事務範圍,在這個範圍內對WCF服務進行連續調用,能夠實現其中一個出現問題總體回滾的效果。因爲WCF依賴於MSDTC,因此首先須要開啓MSDTC服務纔可以經過WCF進行分佈式事務編程。分佈式
這裏我也本身寫了一個典型的銀行轉帳的練習,首先須要創建一個數據庫,數據表只有一張Account表,其中有AccountId和Money兩個int型字段,AccountId爲主鍵。裏面有兩個帳戶:帳戶1有1000,帳戶2有1000。ui
首先,既然是分佈式事務,事務須要從客戶端流轉到服務端,那麼它們之間就應該達成「共識」,也就是說須要對服務契約作手腳。下面定義一個服務契約:spa
[ServiceContract(SessionMode = SessionMode.Required)] public interface IBankService { [OperationContract] [TransactionFlow(TransactionFlowOption.Mandatory)] void OutMoney(int fromAccountId, int money); [OperationContract] [TransactionFlow(TransactionFlowOption.Mandatory)] void InMoney(int toAccountId, int money); }
在轉帳的整個過程中,用戶首先發送一個OutMoney請求,減小帳戶1當中的錢,以後發送InMoney請求,增長帳戶2的錢。顯然這個契約須要一個會話,因此給服務契約增長SessionMode屬性。注意,在實際應用當中轉帳應該做爲一個完整的服務而不是兩個服務方法,這裏只是舉個例子而已。代理
事務的流轉是一個操做行爲,因此須要在操做契約上增長TransactionFlow標記,並設置TransactionFlowOption的值,這個標記是事務的總開關。其值有三個:NotAllowed(默認值,客戶端事務禁止經過該方法流入服務端),Allowed(容許流入事務),Mandatory(必須在事務內調用),這裏將轉帳操做設置爲必須在事務內。code
因爲事務操做必然伴隨着消息交換,因此OneWay操做必然是不支持事務的,即OneWay操做的TransactionFlowOption只能爲NotAllowed(下P143)!htm
接下來實現這個服務操做,代碼以下,其中Repository細節就不貼了:blog
[ServiceBehavior(TransactionIsolationLevel = IsolationLevel.Serializable, TransactionTimeout = "00:05:00", TransactionAutoCompleteOnSessionClose = true)] public class BankService : IBankService { [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = false)] public void OutMoney(int fromAccountId, int money) { try { AccountRepository repository = new AccountRepository(); Account accOut = repository.GetAccountById(fromAccountId); accOut.Money -= money; repository.Save(accOut); } catch (Exception ex) { System.Transactions.Transaction.Current.Rollback(); throw new FaultException(new FaultReason(ex.Message)); } } [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)] public void InMoney(int toAccountId, int money) { try { AccountRepository repository = new AccountRepository(); Account accIn = repository.GetAccountById(toAccountId); accIn.Money += money; repository.Save(accIn); } catch (Exception ex) { System.Transactions.Transaction.Current.Rollback(); throw new FaultException(new FaultReason(ex.Message)); } } }
服務操做的執行是否須要自動登記到事務當中,以及服務操做什麼時候提交,是服務端本身說了算的,因此要在具體的操做上設定操做行爲。這裏咱們用到兩個行爲:TransactionScopeRequired和TransactionAutoComplete,它們都是布爾值,前者用於決定操做是否歸入事務內,默認爲false,這裏須要設置爲true,後者用於決定該操做執行完畢後是否提交事務,因而在第一個操做上設置爲false,第二個操做設置爲true。
在ServiceBehavior上能夠設定事務的行爲,
最後須要在事務拋異常的狀況下回滾。TransactionIsolationLevel用於指定事務隔離級別,默認是Serializable,TransactionTimeout不解釋,TransactionAutoCompleteOnSessionClose表示在會話正常結束時是否自動提交事務,默認爲false,另外還有一個ReleaseServiceInstanceOnTransactionComplete,表示當事務完畢時是否須要釋放服務實例,默認爲false。
寫完了服務端代碼就該改XML了,XML以下:
<configuration> <system.serviceModel> <bindings> <ws2007HttpBinding> <binding name="transactionalTcpBinding" transactionFlow="true" /> </ws2007HttpBinding> </bindings> <behaviors> <serviceBehaviors> <behavior name="metadataBehavior"> <serviceMetadata httpGetEnabled="true" httpGetUrl="http://127.0.0.1:9527/bankservice/metadata" /> </behavior> </serviceBehaviors> </behaviors> <services> <service name="Bank.Service.BankService" behaviorConfiguration="metadataBehavior" > <host> <baseAddresses> <add baseAddress="http://127.0.0.1:9527/"/> </baseAddresses> </host> <endpoint address="bankservice" binding="ws2007HttpBinding" bindingConfiguration="transactionalTcpBinding" contract="Bank.Interface.IBankService" /> </service> </services> </system.serviceModel> </configuration>
WCF是否有能力流轉事務以及事務按照怎樣的協議流轉,是綁定控制的。在WCF當中,除了BasicHttpBinding、NetMsmqBinding和MsmqIntegrationBinding外,都是支持事務傳播的(下P145),即使支持事務,事務流轉也是默認關閉的,因此須要配置綁定的transactionFlow屬性。這裏使用了ws2007HttpBinding,並將其transactionFlow屬性設置爲true。
發佈服務後,在客戶端使用以下代碼調用服務:
static void Main(string[] args) { BankServiceClient proxy = new BankServiceClient(); using (TransactionScope transactionScope = new TransactionScope()) { try { proxy.OutMoney(1, 100); proxy.InMoney(2, 100); transactionScope.Complete(); } catch (Exception ex) { (proxy as ICommunicationObject).Abort(); } } }
這裏定義了一個事務範圍,而且在最後提交了事務,若是出現異常,則關閉服務代理。這樣,在服務過程中若是出現了異常,事務就會回滾了。
十9、可靠會話
此次真的是名副其實地把書讀薄了!下冊書上全是原理,嗯,這裏不抄原理,只用來拷代碼,不過發現書上木有太多現成可用的代碼,因而就稍微總結總結好了。所謂可靠會話就是用於保證消息傳遞有效、有序、不重複的一套機制,WCF對這套機制的實現體如今一個叫ReliableSessionBindingElement的綁定元素上,因此要實現可靠會話,能夠修改綁定,或者手寫綁定。
典型的應用就是文件分段傳輸,若是不實現可靠會話,分段傳輸就可能發生丟包、接收發送順序不一致和重複發送的問題。
WCF已經爲咱們提供了不少支持可靠會話的內置綁定,其中wsHttpBinding、wsFederationBinding、netTcpBinding的可靠會話功能是默認關閉的,wsDualHttpBinding和netNamedPipesBinding是默認開啓的。
啓動可靠會話很簡單,只要在綁定里加上配置:
<bindings> <netTcpBinding> <binding name="reliableNetTcpBinding"> <reliableSession enabled="true"/> </binding> </netTcpBinding> </bindings>
而且在終結點的bindingConfiguration屬性指定這個綁定配置就好了。須要注意客戶端和服務端的綁定配置要一致。
這個綁定配置節有幾個屬性,參考http://msdn.microsoft.com/zh-cn/library/ms731302.aspx,能夠用於作負載控制,不過不管如何,ordered要設置爲true才能開啓可靠會話(P255)。
另外,若是咱們須要某個服務操做必需要保證有序性才能被執行,則須要在ServiceContract接口定義上多打上一個反射標籤:
[DeliveryRequirements(RequireOrderedDelivery = true)]
這樣一來,若是沒有在服務行爲上配置有序,則host時會報異常。