[续《上篇》]通过将TransactionFlowAttribute特性应用在服务契约的某个操作之上,并指定相应的TransactionFlowOption枚举直,仅仅定义了事务流转的策略而已。或者说,通过这种方式确定对事物流转的一种意愿,客户端是否愿意将当前事务流出,服务端是否愿意接受流入的事务,可以通过TransactionFlowAttribute特性进行控制。所以说,服务操作上定义个TransactionFlowAttribute特性是是否进行事务流转的总开关,真正的事务传播是建立在TransactionFlowOption.Allowed或者TransactionFlowOption.Mandatory之上的。

至于WCF框架是否有能力对事物进行流转,按照怎样的协议进行流转,则是通过绑定实现的,现在我们首先看看怎样的绑定具有事务流转的能力。

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

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

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

既然TransactionFlowBindingElement实现了对事物的流转,那么我们就可以根据某个绑定对象的绑定元素集合中是否包含该元素判断绑定是否支持事务流转。为此,我写了如下一个简单的方法,传入相应的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: }

现在,我们通过调用PrintTransactionFlowSupport方法,判断所有的系统绑定是否为事务流转提供支持。从输出结果来看,除了BasicHttpBinding、NetMsmqBinding和MsmqIntegrationBinding三种,其余的系统绑定均包含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)的消息交换模式,所以它们不具有事务流转的能力。但是,即使对于契约的支持事务的绑定类型,事务流转默认也是被关闭的,在真正需要事先事务流转的场景中,需要通过配置或者编成的方式开启该选项。此外,事务流转涉及事务在消息中的格式化问题,而事务的格式化决定于采用的协议。通过《谈谈分布式事务之四: 两种事务处理协议OleTx与WS-AT》我们知道,WCF支持三种不同的事务处理协议:OleTx,WS-AT 1.0和WS-AT 1.0。事务处理协议通过类型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是一个抽象类,定义了三种静态只读属性OleTransactions、WSAtomicTransactionOctober2004和WSAtomicTransaction11,用于获取分别代表OleTx,WS-AT 1.0和WS-AT 1.0三种协议的具体TransactionProtocol对象。这三种具体的TransactionProtocol类型以内部(Internal)类型的方式定义。Default制度属性返回默认的事务处理协议,和OleTransactions属性值一致。

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

   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的事务处理协议,其中WSHttpBinding、WSDualHttpBinding、WSFederationHttpBinding支持的协议是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: }

系统绑定的TransactionFlow和TransactionProtocol属性(仅限于NetTcpBinding和NetNamedPipeBinding)可以通过配置的方式指定。下面的配置中定义了开启了transactionFlow开关的两个绑定(NetTcpBinding和WS2007HttpBinding),并将其中的NetTcpBinding的TransactionProtocol设置成基于WS-AT 1.0的协议(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>

如果现有的系统绑定不能满足你的需要(比如你需要同时采用HTTP传输协议和OleTx事务处理协议),可以通过编程或者配置的方式创建自定的绑定(CustomBinding)。创建支持事务流转的自定义绑定的时候,你需要做的仅仅是将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特性为某个操作设置相应的事务流转策略,绑定决定了实现事务流转的能力和方式,两个的不同组合表现出不同的事务流转行为。在这里,事务的流转包含两个层面的意思,即事务的流出或者发送,以及事务的流入或者接收。对于WCF的客户端框架来说,对于通过TransactionFlowAttribute特性设置的三个选项来说,NotAllowed和Allowed对绑定的事务流转能力没有任何要求,而Madantory则强制要求终结点的绑定能够实现事务的流转(绑定本身能够支持事务流转并且TransactionFlow开关必须开启)。结合上面所介绍的,事务流转选项和绑定类型两两组合所表现出的行为如下面的表格所示(这里的事务绑定表示TransactionFlow开关开启的支持事务流转的绑定)。

事务绑定

非事务绑定

NotAllowed

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

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

Allowed

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

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

Mandatory

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

不合法的组合

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

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

  • 接收的消息中是否具有包含流入事务的SOAP报头;
  • 如果包括需要考虑流入事务在SOAP报头中的XML格式是否与绑定采用的事务处理协议一致;
  • 如果不一致需要考虑事务报头的MustUnderstand属性是True(或1)还是False(或者0)。

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

首先,如果一个服务契约的任何一个操作的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报头中的表示并不符合绑定采用的事务处理协议,由于Mandatory选项在服务端的含义就是强制需要流入一个可以理解的事务,在这种情况下服务端会返回一个Fault消息,并导致客户端抛出异常。同样是对于前面给定义的IBankingService服务契约,如果我们将客户端和服务端终结点的绑定配置成不同的事务处理协议,比如客户端采用默认的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 客户端端和服务端采用不同的事务处理协议导致的异常(Mandatory)

倘若接收到的消息中存在事务报头,并且报头的MustUnderstand属性为True或者1,对于Allowed选项来说,如果采用非事务绑定,或者说虽然采用事务绑定,但是事务报头与绑定采用的事务处理协议不符。在这种情况下,服务端不能有效地理解事务报头,也会向客户端返回一个Fault消息,并导致客户端抛出异常。比如说,我们采用上面提供的配置(客户端和服务端绑定采用不同的事务处理协议),如果我们将服务契约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 客户端端和服务端采用不同的事务处理协议导致的异常(Allowed)

相似情况同样发生在TransactionFlow选项为NotAllowed的时候,所不同的是:即使接收到的事务报头与绑定采用的事务处理协议相匹配,仍然会导致事务报头不能理解的异常。

转载于:https://www.cnblogs.com/liwei823/archive/2011/09/14/2176654.html

WCF事务编程[中篇]相关推荐

  1. WCF分布式开发步步为赢(12):WCF事务机制(Transaction)和分布式事务编程

    今天我们继续学习WCF分布式开发步步为赢系列的12节:WCF事务机制(Transaction)和分布式事务编程.众所周知,应用系统开发过程中,事务是一个重要的概念.它是保证数据与服务可靠性的重要机制. ...

  2. WCF把书读薄(4)——事务编程与可靠会话

    WCF把书读薄(3)--数据契约.消息契约与错误契约 真不愧是老A的书,例子多,而且也讲了不少原理方面的内容,不过越读越觉得压力山大--这次来稍微整理整理事务和可靠会话的内容. 十八.事务编程 WCF ...

  3. WCF服务编程设计规范(6):队列服务、安全和服务总线

    WCF服务编程设计规范(6):队列服务.安全和服务总线.本节整理队列服务(Queue Servuce).服务安全(Service Security)和服务总线(Service Bus)的设计规范. Q ...

  4. WCF服务编程设计规范(9):中文、中英文对照、英文版资料共享下载

    这里我把所有的<WCF服务编程设计规范>这个系列的资料共享出来,包含<WCF编码规范>与<WCF最佳实践>的翻译手稿,提供中文.中英文对照.英文版资料共享Word与 ...

  5. WCF服务编程 学习笔记(1)

    你或许可以使用某一技术实现某些功能,可以按着指定的要求,完成特定的功能,实现某一想要的效果,这表示你可以使用该技术,会使用该技术,但是我们不能停留在使用的层次上,还要了解它们的运行机制,可能有点深了, ...

  6. 徐雷:做最好的自己!写在《mongodb实战》第2版和《WCF服务编程》第4版出版之际...

    做最好的自己:写在<mongodb实战>第2版和<WCF服务编程>第4版出版之际.很久之前我写过一个类似的文章. 这篇文章写给过去的10.也写给未来的自己. 你是否对自己的未来 ...

  7. WCF服务编程(4):《WCF服务编程》第3版中文版翻译结束,即将出版

    原文地址: <WCF服务编程>第4版本翻译完毕!2016年底应该出版 [置顶]↑ WCF服务编程(4):<WCF服务编程>第3版中文版翻译结束,即将出版 ↑ 作为世界范围内最经 ...

  8. WCF分布式开发步步为赢(14):WCF安全编程--基本概念

    WCF安全机制是个非常复杂的问题,因为涉及的知识点较多,所以今天这个文章,会分析进行WCF安全开发应该了解的哪些知识点.如何查看资料.为了更好地理解WCF安全相关知识,我把WCF安全机制主要知识点整理 ...

  9. 19.分布式事务编程

    InnoDB存储引擎存储了对于XA事务的支持,并通过XA事务来支持分布式事务的实现. 1.什么是分布式事务? 分布式事务是指允许多个独立的事务资源(transactional resources)参与 ...

最新文章

  1. 04_Pytorch生态、PyTorch能做什么、PyTorch之Autograd、autograd案例、GPU加速案例
  2. 外网如何访问 Service?- 每天5分钟玩转 Docker 容器技术(139)
  3. linux 写结构体到文件
  4. Linux Redis 安装
  5. 前端学习(1296):第三方模块nodenrm
  6. 【转】Dynamics 365中开发和注册插件介绍
  7. HTTP WS 区别
  8. 计算机专业所需的职业道德,浅议计算机职业道德
  9. flex builder 4.6破解
  10. 网络编程入门(代码很详细)
  11. JAVA 循环结构while简单方法使用
  12. access汇总_Access数据库使用,你都知道吗?
  13. 动态规划_数字的划分
  14. Android中Callable、Future、FutureTask的概念以及几种线程池的使用
  15. 纪念即将逝去的2019
  16. vue搜索(不区分大小写)通用
  17. IDEA多级包创建不分离解决方案
  18. CSR Audio Sink Application User Guide
  19. 前IBM和Cisco执行官Tom Noonan将加入Bakkt担任董事会主席
  20. 计算机应用技术机测,[计算机应用技术]阶段测评1(2016年版)(13页)-原创力文档...

热门文章

  1. Java日期相关类:Date、SimpleDateFormat和Calendar类常用API代码示例
  2. jdbc,mybatis,hibernate各自优缺点及区别
  3. mysql apache php 容器_docker 容器的使用[php 5.6 mysql5.7]
  4. php 求数组合集,PHP数组排序函数合集 以及它们之间的联系分析
  5. 用php编写一个强迫身份认证,php创建基本身份认证站点的实现方法
  6. 三维数据平滑处理_你该如何正确的处理思看科技三维扫描仪得到的数据?
  7. easyui框架前后端交互_easyui前后端分离
  8. 安装不文件不完全_冬日不偷懒 跑步机不完全使用指南
  9. Hashtable源码分析
  10. linux 控制网卡流量,如何管理和控制多网卡 Linux 虚拟机的流量走向