WCF事务编程其实很简单,可以用三句话进行概括:通过服务契约决定事物流转(Transaction Flow)的策略;通过绑定实施事务的流转;通过服务行为控制事务的相关行为。本篇文章着重介绍如果通过TransactionFlowAttribute特性定义事务流转策略。
契约时是一种双边协定,是双方就某个关注点达成的一种共识。对于分布式事务的实现来讲,首先需要解决的是事务流转的问题,即事务将客户端的事务流向服务端。要解决事务流转的问题,需要在事务的发送方和接收方就流转问题达成共识,即双方采用相匹配的事务发送和接收策略。毫无疑问,这样的开关需要定义在服务契约之上,同时事务是基于服务操作的,所以事务流转策略最终应用到操作契约上面。
WCF通过TransactionFlowAttribute特性将相应的事务流转策略关联到某个服务之上,具体来讲,我们在定义服务契约的时候,直接将TransactionFlowAttribute特性应用到相应的操作契约上即可。我们先来看看TransactionFlowAttribute的定义,从下面的代码我们可以看到,TransactionFlowAttribute并仅仅是一个简单的自定义特性,它更是一个操作行为。至于该操作行为对事务流转行为作了怎样的控制,会在后续的文章详细讲述。
1: [AttributeUsage(AttributeTargets.Method)]
2: public sealed class TransactionFlowAttribute : Attribute, IOperationBehavior
3: {
4: //其他成员
5: public TransactionFlowAttribute(TransactionFlowOption transactions);
6: public TransactionFlowOption Transactions { get; }
7: }
当我们将TransactionFlowAttribute特性应用到操作契约的时候,具体事务流转策略通过TransactionFlowOption枚举指定,TransactionFlowOption定义如下。
1: public enum TransactionFlowOption
2: {
3: NotAllowed,
4: Allowed,
5: Mandatory
6: }
TransactionFlowOption一共定义了三选项:NotAllowed、Allowed和Mandatory,它们的分别代表的事务流转策略如下:
- NotAllowed:客户端的事务不允许被流转到服务端;服务端也不会试图去接收流入的事务,这是默认选项;
- Allowed:如果客户端事务存在,则被流转到服务端;服务端会试图去接收流入的事务;
- Mandatory:客户端必须在一个事务中进行服务调用,相应的事务并会被流转到服务端;服务端接收到的消息中必须包含被序列化的事务。
在下面定义的IBankingService服务契约中,我们将TransactionFlowAttribute特性应用到了用于进行转帐操作的Transfer方法之上,并指定事务流转选项为Mandatory。
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: }
对于Mandatory选项,如果客户端在进行服务调用的时候并不存在事务,或者说服务端并没有接收到任何流入的事务(当客户端和服务端采用的契约定义了不匹配的事务流转策略时会出现这种情况下,比如客户端采用NotAllowed选项,客户端则采用Mandatory,那么服务端永远也接收不到流入的事务),都会抛出如图1所示的ProtocolException异常。
图1 在选用Mandatory选项下客户端不存在事务导致的异常
事务从客户端流转到服务端后,服务端可以中止事务,并将结果返回到客户端。从这个意义上讲,事务流转建立在请求/回复消息交换模式(MEP)之上,所以,在一个单向(One-Way)操作契约上,不允许应用TransactionFlowAttribute并指定Allowed或者Mandatory选项。比如下面的服务契约定义是不合法的:
1: [ServiceContract(Namespace = "http://www.artech.com/")]
2: public interface IBankingService
3: {
4: [OperationContract(IsOneWay = true)]
5: [TransactionFlow(TransactionFlowOption.Mandatory)]
6: void Transfer(string accountFrom, string accountTo, double amount);
7: }
如果某个服务实现了这样的服务契约,在进行服务寄宿的时候,会抛出如图2所示的InvalidOperationException异常(如果仔细的话还会看到VS一个汉化的问题:“单项”应该是“单向”,呵呵!!)。
图2 在单向操作上应用TransactionFlowAttribute特性导致的异常