WCF事务编程[中篇]
[續(xù)《上篇》]通過將TransactionFlowAttribute特性應用在服務契約的某個操作之上,并指定相應的TransactionFlowOption枚舉直,僅僅定義了事務流轉的策略而已。或者說,通過這種方式確定對事物流轉的一種意愿,客戶端是否愿意將當前事務流出,服務端是否愿意接受流入的事務,可以通過TransactionFlowAttribute特性進行控制。所以說,服務操作上定義個TransactionFlowAttribute特性是是否進行事務流轉的總開關,真正的事務傳播是建立在TransactionFlowOption.Allowed或者TransactionFlowOption.Mandatory之上的。
至于WCF框架是否有能力對事物進行流轉,按照怎樣的協(xié)議進行流轉,則是通過綁定實現(xiàn)的,現(xiàn)在我們首先看看怎樣的綁定具有事務流轉的能力。
一、綁定對事務流轉的支持
《WCF技術剖析(卷1)》中的第3章對綁定的本質進行了深層次的剖析,閱讀過本章的讀者應該知道:綁定是一系列綁定元素(BindingElement)的有序組合,相應的綁定元素對消息進行相應的處理以實現(xiàn)特定的目標,比如MessageEncodingBindingElement實現(xiàn)對消息的編碼和解碼,TransportBindingElement實現(xiàn)對消息的傳輸。
消息交換是WCF進行通信的唯一手段,任何需要傳輸?shù)臄?shù)據(jù)最終都需要最為消息的一部分。對象事務流轉來說,客戶端需要將當前事務進行序列化并嵌入到消息中;服務端則需要從接收到的消息中提取事務相關信息,反序列化以重建事務。這樣的操作同樣實現(xiàn)在一個綁定元素中,即TransactionFlowBindingElement。
既然TransactionFlowBindingElement實現(xiàn)了對事物的流轉,那么我們就可以根據(jù)某個綁定對象的綁定元素集合中是否包含該元素判斷綁定是否支持事務流轉。為此,我寫了如下一個簡單的方法,傳入相應的Binding對象,打印出相應的綁定類型是否支持事務流轉:
1: static void PrintTransactionFlowSupport(Binding binding) 2: { 3: TransactionFlowBindingElement transactionFlowElement = binding.CreateBindingElements().Find< TransactionFlowBindingElement>(); 4: Console.WriteLine("{0,-30} {1}",binding.GetType().Name,transactionFlowElement!=null?"Yes":"No"); 5: }現(xiàn)在,我們通過調用PrintTransactionFlowSupport方法,判斷所有的系統(tǒng)綁定是否為事務流轉提供支持。從輸出結果來看,除了BasicHttpBinding、NetMsmqBinding和MsmqIntegrationBinding三種,其余的系統(tǒng)綁定均包含TransactionFlowBindingElement綁定元素,也就是說它們均具有對事務就是傳播的能力。
1: class Program 2: { 3: static void Main(string[] args) 4: { 5: Console.WriteLine("{0,-30} {1}", "Binding", "Transaction Flow"); 6: Console.WriteLine("--------------------------------------------"); 7: //BasicHttpBinding 8: PrintTransactionFlowSupport(new BasicHttpBinding()); 9: 10: //WS Binding 11: PrintTransactionFlowSupport(new WSHttpBinding()); 12: PrintTransactionFlowSupport(new WS2007HttpBinding()); 13: PrintTransactionFlowSupport(new WSDualHttpBinding()); 14: PrintTransactionFlowSupport(new WSFederationHttpBinding()); 15: PrintTransactionFlowSupport(new WS2007FederationHttpBinding()); 16: 17: //TCP and IPC Binding 18: PrintTransactionFlowSupport(new NetTcpBinding()); 19: PrintTransactionFlowSupport(new NetNamedPipeBinding()); 20: //MSMQ Binding 21: PrintTransactionFlowSupport(new NetMsmqBinding()); 22: PrintTransactionFlowSupport(new MsmqIntegrationBinding()); 23: } 24: }輸出結果:
Binding Transaction Flow ----------------------------------------------- BasicHttpBinding No WSHttpBinding Yes WS2007HttpBinding Yes WSDualHttpBinding Yes WSFederationHttpBinding Yes WS2007FederationHttpBinding Yes NetTcpBinding Yes NetNamedPipeBinding Yes NetMsmqBinding No MsmqIntegrationBinding No由于BasicHttpBinding基于WS-I Basic Profile標準的綁定,而兩個基于MSQM的綁定(NetMsmqBinding和MsmqIntegrationBinding)只能采用單向(One-Way)的消息交換模式,所以它們不具有事務流轉的能力。但是,即使對于契約的支持事務的綁定類型,事務流轉默認也是被關閉的,在真正需要事先事務流轉的場景中,需要通過配置或者編成的方式開啟該選項。此外,事務流轉涉及事務在消息中的格式化問題,而事務的格式化決定于采用的協(xié)議。通過《談談分布式事務之四: 兩種事務處理協(xié)議OleTx與WS-AT》我們知道,WCF支持三種不同的事務處理協(xié)議:OleTx,WS-AT 1.0和WS-AT 1.0。事務處理協(xié)議通過類型TransactionProtocol類型表示,TransactionProtocol定義如下:
1: public abstract class TransactionProtocol 2: { 3: public static TransactionProtocol Default { get; } 4: 5: public static TransactionProtocol OleTransactions { get; } 6: public static TransactionProtocol WSAtomicTransactionOctober2004 { get; } 7: public static TransactionProtocol WSAtomicTransaction11 { get; } 8: }TransactionProtocol是一個抽象類,定義了三種靜態(tài)只讀屬性OleTransactions、WSAtomicTransactionOctober2004和WSAtomicTransaction11,用于獲取分別代表OleTx,WS-AT 1.0和WS-AT 1.0三種協(xié)議的具體TransactionProtocol對象。這三種具體的TransactionProtocol類型以內部(Internal)類型的方式定義。Default制度屬性返回默認的事務處理協(xié)議,和OleTransactions屬性值一致。
對于NetTcpBinding和NetNamedPipeBinding來說,我們可以通過屬性TransactionFlow設置或者獲取綁定是否支持事務流轉的開關,并通過TransactionProtocol屬性設置或者獲取綁定支持的事務處理協(xié)議。
1: public class NetTcpBinding : Binding, IBindingRuntimePreferences 2: { 3: //其他成員 4: public bool TransactionFlow { get; set; } 5: public TransactionProtocol TransactionProtocol { get; set; } 6: } 7: 8: public class NetNamedPipeBinding : Binding, IBindingRuntimePreferences 9: { 10: //其他成員 11: public bool TransactionFlow { get; set; } 12: public TransactionProtocol TransactionProtocol { get; set; } 13: }而對于基于WS的綁定來說,由于綁定本身就是為跨平臺和互操作涉及的,所以僅僅支持基于WS-AT的事務處理協(xié)議,其中WSHttpBinding、WSDualHttpBinding、WSFederationHttpBinding支持的協(xié)議是WS-AT 1.0,而WS2007HttpBinding和WS2007FederationHttpBinding支持的是WS-AT 1.1。所以,它們僅僅具有TransactionFlow屬性,并沒有TransactionProtocol屬性,該屬性定義在它們的基類WSHttpBindingBase上面:
1: public abstract class WSHttpBindingBase : Binding, IBindingRuntimePreferences 2: { 3: //其他成員 4: public bool TransactionFlow { get; set; } 5: }系統(tǒng)綁定的TransactionFlow和TransactionProtocol屬性(僅限于NetTcpBinding和NetNamedPipeBinding)可以通過配置的方式指定。下面的配置中定義了開啟了transactionFlow開關的兩個綁定(NetTcpBinding和WS2007HttpBinding),并將其中的NetTcpBinding的TransactionProtocol設置成基于WS-AT 1.0的協(xié)議(transactionProtocol="WSAtomicTransactionOctober2004")。
1: <?xml version="1.0" encoding="utf-8" ?> 2: <configuration> 3: <system.serviceModel> 4: <bindings> 5: <netTcpBinding> 6: <binding name="transactionalTcpBinding" transactionFlow="true" transactionProtocol="WSAtomicTransactionOctober2004" /> 7: </netTcpBinding> 8: <ws2007HttpBinding> 9: <binding name="transactionalHttpBinding" transactionFlow="true" /> 10: </ws2007HttpBinding> 11: </bindings> 12: <services> 13: <service name="Artech.TransactionalServices.BankingService"> 14: <endpoint address="net.tcp://127.0.0.1/bankingservice" binding="netTcpBinding" 15: bindingConfiguration="transactionalTcpBinding" contract="Artech.TransactionalServices.IBankingService" /> 16: <endpoint address="http://127.0.0.1/bankingservice" binding="ws2007HttpBinding" 17: bindingConfiguration="transactionalHttpBinding" contract="Artech.TransactionalServices.IBankingService" /> 18: </service> 19: </services> 20: </system.serviceModel> 21: </configuration>如果現(xiàn)有的系統(tǒng)綁定不能滿足你的需要(比如你需要同時采用HTTP傳輸協(xié)議和OleTx事務處理協(xié)議),可以通過編程或者配置的方式創(chuàng)建自定的綁定(CustomBinding)。創(chuàng)建支持事務流轉的自定義綁定的時候,你需要做的僅僅是將TransactionFlowBindingElement添加到綁定元素集合中,并設置TransactionFlow和TransactionProtocol屬性即可。下面的配置就定義了這樣一個基于OleTx的HTTP綁定。
1: <?xml version="1.0" encoding="utf-8" ?> 2: <configuration> 3: <system.serviceModel> 4: <bindings> 5: <customBinding> 6: <binding name="transactionalBinding"> 7: <textMessageEncoding /> 8: <transactionFlow transactionProtocol="OleTransactions"/> 9: <httpTransport /> 10: </binding> 11: </customBinding> 12: </bindings> 13: <services> 14: <service name="Artech.TransactionalServices.BankingService"> 15: <endpoint address="http://127.0.0.1/bankingservice" binding="customBinding" 16: bindingConfiguration="transactionalBinding" contract="Artech.TransactionalServices.IBankingService" /> 17: </service> 18: </services> 19: </system.serviceModel> 20: </configuration>二、 綁定與TransactionFlow設置
通過應用TransactionFlowAttribute特性為某個操作設置相應的事務流轉策略,綁定決定了實現(xiàn)事務流轉的能力和方式,兩個的不同組合表現(xiàn)出不同的事務流轉行為。在這里,事務的流轉包含兩個層面的意思,即事務的流出或者發(fā)送,以及事務的流入或者接收。對于WCF的客戶端框架來說,對于通過TransactionFlowAttribute特性設置的三個選項來說,NotAllowed和Allowed對綁定的事務流轉能力沒有任何要求,而Madantory則強制要求終結點的綁定能夠實現(xiàn)事務的流轉(綁定本身能夠支持事務流轉并且TransactionFlow開關必須開啟)。結合上面所介紹的,事務流轉選項和綁定類型兩兩組合所表現(xiàn)出的行為如下面的表格所示(這里的事務綁定表示TransactionFlow開關開啟的支持事務流轉的綁定)。| 事務綁定 | 非事務綁定 | |
| NotAllowed | 當前事務不需要存在,存在的當前事務不會被流出 | 當前事務不需要存在,存在的當前事務不會被流出 |
| Allowed | 當前事務不需要存在,存在的當前事務會被流出 | 當前事務不需要存在,存在的當前事務不會被流出 |
| Mandatory | 當前事務必須存在,存在的當前事務會被流出 | 不合法的組合 |
對于一個服務契約來說,如果任何一個操作的TransactionFlow選項被定義成Mandatory,相應終結點所采用的綁定必須是事務綁定(接下來我們將本身支持事務流轉,并開啟了TransactionFlow開關的綁定稱為事務綁定)。下面的代碼和配置中,通過TransactionFlowAttribute將唯一的Transfer操作的事務流轉選項設置為Mandatory,并選用不支持事務流轉的BasicHttpBinding。當使用創(chuàng)建的ChannelFactory<TChannel>創(chuàng)建服務代理的時候,拋出如圖1所示的InvalidOperationException異常。
1: [ServiceContract(Namespace="http://www.artech.com/")] 2: public interface IBankingService 3: { 4: [OperationContract] 5: [TransactionFlow(TransactionFlowOption.Mandatory)] 6: void Transfer(string accountFrom, string accountTo, double amount); 7: } 1: <?xml version="1.0" encoding="utf-8" ?> 2: <configuration> 3: <system.serviceModel> 4: <client> 5: <endpoint address="http://127.0.0.1:3721/bankingservice" binding="basicHttpBinding" contract="Artech.TransactionalServices.IBankingService" name="bankingservice" /> 6: </client> 7: </system.serviceModel> 8: </configuration>圖1 客戶端在Mandatory事務流轉選項情況下采用非事務綁定拋出的異常
上面所說的是不同的事務流轉選項和綁定類型在客戶端的表現(xiàn)行為,現(xiàn)在我們將目光轉移到服務端。較之客戶端,服務的情況要稍微復雜一些,處理考慮事務流轉選項和綁定對事務流轉的支持之外,還需要考慮以下三個因素:
- 接收的消息中是否具有包含流入事務的SOAP報頭;
- 如果包括需要考慮流入事務在SOAP報頭中的XML格式是否與綁定采用的事務處理協(xié)議一致;
- 如果不一致需要考慮事務報頭的MustUnderstand屬性是True(或1)還是False(或者0)。
WCF服務端服務流轉表現(xiàn)出來的最終行為決定于上述的五個要素,下面的表格流出了它們之間不同的組合最終表現(xiàn)出來的事務處理行為。
首先,如果一個服務契約的任何一個操作的TransactionFlow選項定義成Mandatory,那么強制要求相應的終結點采用事務綁定。比如說,同樣對于上面定義的IBankingService服務契約(TransactionFlow),但是使用默認的WS2007HttpBinding(默認情況下TransactionFlow是關閉的),在進行服務寄宿的時候,會拋出如圖2所示的InvalidOperationException異常。
1: <?xml version="1.0" encoding="utf-8" ?> 2: <configuration> 3: <system.serviceModel> 4: <services> 5: <service name="Artech.TransactionalServices.BankingService"> 6: <endpoint address="http://127.0.0.1:3721/bankingservice" binding="ws2007HttpBinding" contract="Artech.TransactionalServices.IBankingService" /> 7: </service> 8: </services> 9: </system.serviceModel> 10: </configuration>圖2 客戶端在Mandatory事務流轉選項情況下采用非事務綁定拋出的異常
其次,同樣對于TransactionFlow選項為Mandatory的操作,如果接收的消息并不包含流入事務的SOAP報頭,或者說流入的事務在SOAP報頭中的表示并不符合綁定采用的事務處理協(xié)議,由于Mandatory選項在服務端的含義就是強制需要流入一個可以理解的事務,在這種情況下服務端會返回一個Fault消息,并導致客戶端拋出異常。同樣是對于前面給定義的IBankingService服務契約,如果我們將客戶端和服務端終結點的綁定配置成不同的事務處理協(xié)議,比如客戶端采用默認的OleTx,服務端則采用WS-AT 1.1。客戶端在進行服務調用的時候,會拋出如圖3所示的ProtocolException異常。
客戶端配置:
1: <?xml version="1.0" encoding="utf-8" ?> 2: <configuration> 3: <system.serviceModel> 4: <bindings> 5: <netTcpBinding> 6: <binding name="nonTransactionalBinding" transactionFlow="true"/> 7: </netTcpBinding> 8: </bindings> 9: <client> 10: <endpoint address="net.tcp://127.0.0.1:3721/bankingservice" binding="netTcpBinding" bindingConfiguration="nonTransactionalBinding" contract="Artech.TransactionalServices.IBankingService" name="bankingservice" /> 11: </client> 12: </system.serviceModel> 13: </configuration>服務端配置:
1: <?xml version="1.0" encoding="utf-8" ?> 2: <configuration> 3: <system.serviceModel> 4: <bindings> 5: <netTcpBinding> 6: <binding name="transactionalBinding" transactionFlow="true" transactionProtocol="WSAtomicTransaction11" /> 7: </netTcpBinding> 8: </bindings> 9: <services> 10: <service name="Artech.TransactionalServices.BankingService"> 11: <endpoint address="net.tcp://127.0.0.1:3721/bankingservice" binding="netTcpBinding" 12: bindingConfiguration="transactionalBinding" contract="Artech.TransactionalServices.IBankingService" /> 13: </service> 14: </services> 15: </system.serviceModel> 16: </configuration>圖3 客戶端端和服務端采用不同的事務處理協(xié)議導致的異常(Mandatory)
倘若接收到的消息中存在事務報頭,并且報頭的MustUnderstand屬性為True或者1,對于Allowed選項來說,如果采用非事務綁定,或者說雖然采用事務綁定,但是事務報頭與綁定采用的事務處理協(xié)議不符。在這種情況下,服務端不能有效地理解事務報頭,也會向客戶端返回一個Fault消息,并導致客戶端拋出異常。比如說,我們采用上面提供的配置(客戶端和服務端綁定采用不同的事務處理協(xié)議),如果我們將服務契約IBankingService的Transfer操作的TransactionFlow選項設置為Allowed,客戶端在進行服務調用的時候會拋出如圖4所示的ProtocolException異常。但是,如果MustUnderstand屬性為False或者0,事務報頭會被忽略。
1: [ServiceContract(Namespace = "http://www.artech.com/")] 2: public interface IBankingService 3: { 4: [OperationContract] 5: [TransactionFlow(TransactionFlowOption.Allowed)] 6: void Transfer(string accountFrom, string accountTo, double amount); 7: }圖4 客戶端端和服務端采用不同的事務處理協(xié)議導致的異常(Allowed)
相似情況同樣發(fā)生在TransactionFlow選項為NotAllowed的時候,所不同的是:即使接收到的事務報頭與綁定采用的事務處理協(xié)議相匹配,仍然會導致事務報頭不能理解的異常。
轉載于:https://www.cnblogs.com/liwei823/archive/2011/09/14/2176654.html
總結
以上是生活随笔為你收集整理的WCF事务编程[中篇]的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【转自CDDN】随笔:sysobject
- 下一篇: 让IE6支持图片半透明