配色: 字号:
WCF事务编程
2016-09-30 | 阅:  转:  |  分享 
  
WCF事务编程

一、绑定对事务流转的支持

《WCF技术剖析(卷1)》中的第3章对绑定的本质进行了深层次的剖析,阅读过本章的读者应该知道:绑定是一系列绑定元素(BindingElement)的有序组合,相应的绑定元素对消息进行相应的处理以实现特定的目标,比如MessageEncodingBindingElement实现对消息的编码和解码,TransportBindingElement实现对消息的传输。



消息交换是WCF进行通信的唯一手段,任何需要传输的数据最终都需要最为消息的一部分。对象事务流转来说,客户端需要将当前事务进行序列化并嵌入到消息中;服务端则需要从接收到的消息中提取事务相关信息,反序列化以重建事务。这样的操作同样实现在一个绑定元素中,即TransactionFlowBindingElement。



既然TransactionFlowBindingElement实现了对事物的流转,那么我们就可以根据某个绑定对象的绑定元素集合中是否包含该元素判断绑定是否支持事务流转。为此,我写了如下一个简单的方法,传入相应的Binding对象,打印出相应的绑定类型是否支持事务流转:



1:staticvoidPrintTransactionFlowSupport(Bindingbinding)

2:{

3:TransactionFlowBindingElementtransactionFlowElement=binding.CreateBindingElements().Find();

4:Console.WriteLine("{0,-30}{1}",binding.GetType().Name,transactionFlowElement!=null?"Yes":"No");

5:}

现在,我们通过调用PrintTransactionFlowSupport方法,判断所有的系统绑定是否为事务流转提供支持。从输出结果来看,除了BasicHttpBinding、NetMsmqBinding和MsmqIntegrationBinding三种,其余的系统绑定均包含TransactionFlowBindingElement绑定元素,也就是说它们均具有对事务就是传播的能力。



1:classProgram

2:{

3:staticvoidMain(string[]args)

4:{

5:Console.WriteLine("{0,-30}{1}","Binding","TransactionFlow");

6:Console.WriteLine("--------------------------------------------");

7://BasicHttpBinding

8:PrintTransactionFlowSupport(newBasicHttpBinding());

9:

10://WSBinding

11:PrintTransactionFlowSupport(newWSHttpBinding());

12:PrintTransactionFlowSupport(newWS2007HttpBinding());

13:PrintTransactionFlowSupport(newWSDualHttpBinding());

14:PrintTransactionFlowSupport(newWSFederationHttpBinding());

15:PrintTransactionFlowSupport(newWS2007FederationHttpBinding());

16:

17://TCPandIPCBinding

18:PrintTransactionFlowSupport(newNetTcpBinding());

19:PrintTransactionFlowSupport(newNetNamedPipeBinding());

20://MSMQBinding

21:PrintTransactionFlowSupport(newNetMsmqBinding());

22:PrintTransactionFlowSupport(newMsmqIntegrationBinding());

23:}

24:}

输出结果:



BindingTransactionFlow

-----------------------------------------------

BasicHttpBindingNo

WSHttpBindingYes

WS2007HttpBindingYes

WSDualHttpBindingYes

WSFederationHttpBindingYes

WS2007FederationHttpBindingYes

NetTcpBindingYes

NetNamedPipeBindingYes

NetMsmqBindingNo

MsmqIntegrationBindingNo

由于BasicHttpBinding基于WS-IBasicProfile标准的绑定,而两个基于MSQM的绑定(NetMsmqBinding和MsmqIntegrationBinding)只能采用单向(One-Way)的消息交换模式,所以它们不具有事务流转的能力。但是,即使对于契约的支持事务的绑定类型,事务流转默认也是被关闭的,在真正需要事先事务流转的场景中,需要通过配置或者编成的方式开启该选项。此外,事务流转涉及事务在消息中的格式化问题,而事务的格式化决定于采用的协议。通过《谈谈分布式事务之四:两种事务处理协议OleTx与WS-AT》我们知道,WCF支持三种不同的事务处理协议:OleTx,WS-AT1.0和WS-AT1.0。事务处理协议通过类型TransactionProtocol类型表示,TransactionProtocol定义如下:



1:publicabstractclassTransactionProtocol

2:{

3:publicstaticTransactionProtocolDefault{get;}

4:

5:publicstaticTransactionProtocolOleTransactions{get;}

6:publicstaticTransactionProtocolWSAtomicTransactionOctober2004{get;}

7:publicstaticTransactionProtocolWSAtomicTransaction11{get;}

8:}

TransactionProtocol是一个抽象类,定义了三种静态只读属性OleTransactions、WSAtomicTransactionOctober2004和WSAtomicTransaction11,用于获取分别代表OleTx,WS-AT1.0和WS-AT1.0三种协议的具体TransactionProtocol对象。这三种具体的TransactionProtocol类型以内部(Internal)类型的方式定义。Default制度属性返回默认的事务处理协议,和OleTransactions属性值一致。



对于NetTcpBinding和NetNamedPipeBinding来说,我们可以通过属性TransactionFlow设置或者获取绑定是否支持事务流转的开关,并通过TransactionProtocol属性设置或者获取绑定支持的事务处理协议。



1:publicclassNetTcpBinding:Binding,IBindingRuntimePreferences

2:{

3://其他成员

4:publicboolTransactionFlow{get;set;}

5:publicTransactionProtocolTransactionProtocol{get;set;}

6:}

7:

8:publicclassNetNamedPipeBinding:Binding,IBindingRuntimePreferences

9:{

10://其他成员

11:publicboolTransactionFlow{get;set;}

12:publicTransactionProtocolTransactionProtocol{get;set;}

13:}

而对于基于WS的绑定来说,由于绑定本身就是为跨平台和互操作涉及的,所以仅仅支持基于WS-AT的事务处理协议,其中WSHttpBinding、WSDualHttpBinding、WSFederationHttpBinding支持的协议是WS-AT1.0,而WS2007HttpBinding和WS2007FederationHttpBinding支持的是WS-AT1.1。所以,它们仅仅具有TransactionFlow属性,并没有TransactionProtocol属性,该属性定义在它们的基类WSHttpBindingBase上面:



1:publicabstractclassWSHttpBindingBase:Binding,IBindingRuntimePreferences

2:{

3://其他成员

4:publicboolTransactionFlow{get;set;}

5:}

系统绑定的TransactionFlow和TransactionProtocol属性(仅限于NetTcpBinding和NetNamedPipeBinding)可以通过配置的方式指定。下面的配置中定义了开启了transactionFlow开关的两个绑定(NetTcpBinding和WS2007HttpBinding),并将其中的NetTcpBinding的TransactionProtocol设置成基于WS-AT1.0的协议(transactionProtocol="WSAtomicTransactionOctober2004")。



1:

2:

3:

4:

5:

6:

7:


8:

9:

10:


11:


12:

13:

14:
15:bindingConfiguration="transactionalTcpBinding"contract="Artech.TransactionalServices.IBankingService"/>

16:
17:bindingConfiguration="transactionalHttpBinding"contract="Artech.TransactionalServices.IBankingService"/>

18:

19:


20:


21:


如果现有的系统绑定不能满足你的需要(比如你需要同时采用HTTP传输协议和OleTx事务处理协议),可以通过编程或者配置的方式创建自定的绑定(CustomBinding)。创建支持事务流转的自定义绑定的时候,你需要做的仅仅是将TransactionFlowBindingElement添加到绑定元素集合中,并设置TransactionFlow和TransactionProtocol属性即可。下面的配置就定义了这样一个基于OleTx的HTTP绑定。



1:

2:

3:

4:

5:

6:

7:

8:

9:

10:

11:


12:


13:

14:

15:
16:bindingConfiguration="transactionalBinding"contract="Artech.TransactionalServices.IBankingService"/>

17:

18:


19:


20:


二、绑定与TransactionFlow设置

通过应用TransactionFlowAttribute特性为某个操作设置相应的事务流转策略,绑定决定了实现事务流转的能力和方式,两个的不同组合表现出不同的事务流转行为。在这里,事务的流转包含两个层面的意思,即事务的流出或者发送,以及事务的流入或者接收。对于WCF的客户端框架来说,对于通过TransactionFlowAttribute特性设置的三个选项来说,NotAllowed和Allowed对绑定的事务流转能力没有任何要求,而Madantory则强制要求终结点的绑定能够实现事务的流转(绑定本身能够支持事务流转并且TransactionFlow开关必须开启)。结合上面所介绍的,事务流转选项和绑定类型两两组合所表现出的行为如下面的表格所示(这里的事务绑定表示TransactionFlow开关开启的支持事务流转的绑定)。



事务绑定



非事务绑定



NotAllowed



当前事务不需要存在,存在的当前事务不会被流出



当前事务不需要存在,存在的当前事务不会被流出



Allowed



当前事务不需要存在,存在的当前事务会被流出



当前事务不需要存在,存在的当前事务不会被流出



Mandatory



当前事务必须存在,存在的当前事务会被流出



不合法的组合



对于一个服务契约来说,如果任何一个操作的TransactionFlow选项被定义成Mandatory,相应终结点所采用的绑定必须是事务绑定(接下来我们将本身支持事务流转,并开启了TransactionFlow开关的绑定称为事务绑定)。下面的代码和配置中,通过TransactionFlowAttribute将唯一的Transfer操作的事务流转选项设置为Mandatory,并选用不支持事务流转的BasicHttpBinding。当使用创建的ChannelFactory创建服务代理的时候,抛出如图1所示的InvalidOperationException异常。



1:[ServiceContract(Namespace="http://www.artech.com/")]

2:publicinterfaceIBankingService

3:{

4:[OperationContract]

5:[TransactionFlow(TransactionFlowOption.Mandatory)]

6:voidTransfer(stringaccountFrom,stringaccountTo,doubleamount);

7:}

1:

2:

3:

4:

5:

6:


7:


8:


image



图1客户端在Mandatory事务流转选项情况下采用非事务绑定抛出的异常



上面所说的是不同的事务流转选项和绑定类型在客户端的表现行为,现在我们将目光转移到服务端。较之客户端,服务的情况要稍微复杂一些,处理考虑事务流转选项和绑定对事务流转的支持之外,还需要考虑以下三个因素:



接收的消息中是否具有包含流入事务的SOAP报头;

如果包括需要考虑流入事务在SOAP报头中的XML格式是否与绑定采用的事务处理协议一致;

如果不一致需要考虑事务报头的MustUnderstand属性是True(或1)还是False(或者0)。

WCF服务端服务流转表现出来的最终行为决定于上述的五个要素,下面的表格流出了它们之间不同的组合最终表现出来的事务处理行为。



image



首先,如果一个服务契约的任何一个操作的TransactionFlow选项定义成Mandatory,那么强制要求相应的终结点采用事务绑定。比如说,同样对于上面定义的IBankingService服务契约(TransactionFlow),但是使用默认的WS2007HttpBinding(默认情况下TransactionFlow是关闭的),在进行服务寄宿的时候,会抛出如图2所示的InvalidOperationException异常。



1:

2:

3:

4:

5:

6:

7:

8:


9:


10:


image



图2客户端在Mandatory事务流转选项情况下采用非事务绑定抛出的异常



其次,同样对于TransactionFlow选项为Mandatory的操作,如果接收的消息并不包含流入事务的SOAP报头,或者说流入的事务在SOAP报头中的表示并不符合绑定采用的事务处理协议,由于Mandatory选项在服务端的含义就是强制需要流入一个可以理解的事务,在这种情况下服务端会返回一个Fault消息,并导致客户端抛出异常。同样是对于前面给定义的IBankingService服务契约,如果我们将客户端和服务端终结点的绑定配置成不同的事务处理协议,比如客户端采用默认的OleTx,服务端则采用WS-AT1.1。客户端在进行服务调用的时候,会抛出如图3所示的ProtocolException异常。



客户端配置:



1:

2:

3:

4:

5:

6:

7:


8:


9:

10:

11:


12:


13:


服务端配置:



1:

2:

3:

4:

5:

6:

7:


8:


9:

10:

11:
12:bindingConfiguration="transactionalBinding"contract="Artech.TransactionalServices.IBankingService"/>

13:

14:


15:


16:


image



图3客户端端和服务端采用不同的事务处理协议导致的异常(Mandatory)



倘若接收到的消息中存在事务报头,并且报头的MustUnderstand属性为True或者1,对于Allowed选项来说,如果采用非事务绑定,或者说虽然采用事务绑定,但是事务报头与绑定采用的事务处理协议不符。在这种情况下,服务端不能有效地理解事务报头,也会向客户端返回一个Fault消息,并导致客户端抛出异常。比如说,我们采用上面提供的配置(客户端和服务端绑定采用不同的事务处理协议),如果我们将服务契约IBankingService的Transfer操作的TransactionFlow选项设置为Allowed,客户端在进行服务调用的时候会抛出如图4所示的ProtocolException异常。但是,如果MustUnderstand属性为False或者0,事务报头会被忽略。



1:[ServiceContract(Namespace="http://www.artech.com/")]

2:publicinterfaceIBankingService

3:{

4:[OperationContract]

5:[TransactionFlow(TransactionFlowOption.Allowed)]

6:voidTransfer(stringaccountFrom,stringaccountTo,doubleamount);

7:}

献花(0)
+1
(本文系thedust79首藏)