首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >WCF技术剖析之三十三:你是否了解WCF事务框架体系内部的工作机制?[上篇]

WCF技术剖析之三十三:你是否了解WCF事务框架体系内部的工作机制?[上篇]

作者头像
蒋金楠
发布2018-01-16 17:38:50
5170
发布2018-01-16 17:38:50
举报
文章被收录于专栏:大内老A大内老A

WCF事务编程主要涉及到这么三个方面:通过服务(操作)契约确定TransactionFlow的策略;通过事务绑定实现事务流转;通过服务操作行为控制事务的自动登记(Enlistment)行为,以及对事务超时时限、隔离级别和实例行为的设定。那么,在WCF内部这三者之间究竟是如何通过相互协作实现分布式事务的呢?这就是本篇文章所要讲述的内容,先来看看应用于服务契约中的某个操作的TransactionFlowAttribute到底为我们作了什么。

一、TransactionFlowAttribute:将TransactionFlow选项存入绑定上下文(BindingContext)

通过前面的介绍,我们知道了通过将TransactionFlowAttribute特性应用于服务契约的某个操作,实际上是指定了基于该操作事务流转的策略(NotAllowed、Allowed和Mandatory)。绑定最终需要根据设置的TransactionFlow选项,决定是否对事务实施流转,即客户端是否需要将当前事务进行序列化并嵌入到出栈消息(Outgoing Message)中发送出去;服务端是否需要从入栈消息(Incoming Message)中提取事务相关信息并重新构建事务。那么两者是如何联系在一起的呢?

在介绍TransactionFlowAttribute特性的时候,我们说过TransactionFlowAttribute并不是一个简单的特性,它是一个实现了IOperationBehavior接口的操作行为。IOperationBehavior接口定义了4个方法(ApplyClientBehavior、ApplyDispatchBehavior、Validate和AddBindingParameters),处理AddBindingParameters,其余三个都是空操作。大体上,TransactionFlowAttribute的定义可以通过下面的伪代码表示。

   1: [AttributeUsage(AttributeTargets.Method)]
   2: public sealed class TransactionFlowAttribute : Attribute, IOperationBehavior
   3: {    
   4:     public TransactionFlowAttribute(TransactionFlowOption transactions);
   5:     void IOperationBehavior.AddBindingParameters(OperationDescription description, BindingParameterCollection parameters)
   6:     {
   7:         //将TransactionFlow选项(NotAllwed、Allowed和Mandatory)存入绑定上下文
   8:         AddTransactionFlowOptionInBindingContext();
   9:     }
  10:     void IOperationBehavior.ApplyClientBehavior(OperationDescription description, ClientOperation proxy){ }
  11:     void IOperationBehavior.ApplyDispatchBehavior(OperationDescription description, DispatchOperation dispatch) { }
  12:     void IOperationBehavior.Validate(OperationDescription description) { }
  13:  
  14:     public TransactionFlowOption Transactions { get; }
  15: }

也就是通过AddBindingParameters这个方法,将设置的TransactionFlow选项传入了绑定上下文(Binding Context),使得通过绑定创建的信道(Channel)可以获取该选项,并根据相应的值控制自身消息处理的行为。在这里,真正使用到该TransactionFlow选项的信道,就是通过事务绑定的TransactionFlowBindingElement创建的事务信道。关于绑定、绑定元素和信道之间的关系,在《WCF技术剖析(卷1)》的第3章有详细的介绍。

二、 事务绑定:实现事务的流转

由于消息交换是WCF进行通信的唯一手段,所以事务的流转最终需要将事务本身作为消息的一部分进行传输。WCF采用不同的事务处理协议(OleTx和WS-AT),反映在消息交换上就是采用怎样的格式对事务进行格式化,以及将格式化的事务信息与消息(主要是SOAP)进行绑定。在WCF的整个事务处理体系结构中,事务的格式化和消息绑定的操作通过事务绑定实现。

我们所说的事务绑定就是包含有TransactionFlowBindingElement绑定元素,并且TransactionFlow开关被开启的绑定。对于客户端来说,TransactionFlowBindingElement创建事务信道工厂(TransactionChannelFactory),而基于不同的信道形状(Channel Shape)和对会话的支持,事务信道工厂会创建相应的事务信道,比如事务输出信道(TransactionOutputChannel)、事务请求信道(TransactionRequestChannel)和事务双工信道(TransactionDuplexChannel)等等。对于服务端来说,TransactionFlowBindingElement会创建事务信道监听器(TransactionChannelListener),与客户端类似,事务信道监听器也会创建基于不同信道形状(Channel Shape)和对会话的支持的事务信道,比如事务输入信道(TransactionInputChannel)、事务回复信道(TransactionReplyChannel)和事务双工信道(TransactionDuplexChannel)等。事务流转相关的绑定元素、绑定管理器(信道工厂和信道监听器)和信道之间的关系如图1所示。

image
image

图1 事务流转相关的绑定元素、信道管理器和信道结构

客户端的事务信道需要将当前事务写入消息,而服务端的事务信道则需要将流入的事务从服务中读出来。WCF将事务的读写操作定义在一个称为TransactionFormatter的类型中。不过,这是一个内部(Internal)类型不能直接使用。TransactionFormatter的定义大体上如下面的代码所示,其中ReadTransaction和WriteTransaction分别实现对事务的读取和写入操作。事务通过TransactionInfo对象的形式被读取出来,TransactionInfo是也是一个内部对象,我们可以通过调用UnmarshalTransaction得到真正的Transaction对象。

   1: internal abstract class TransactionFormatter
   2: {   
   3:     //其他成员
   4:     public abstract TransactionInfo ReadTransaction(Message message);
   5:     public abstract void WriteTransaction(Transaction transaction, Message message);
   6: }
   7: internal abstract class TransactionInfo
   8: {
   9:     //其他成员
  10:     public abstract Transaction UnmarshalTransaction();
  11: }

如果采用不同事务处理协议,相同的事务需要按照不同的方式进行格式化,所以WCF事务体系内部创建了继承自TransactionFormatter的三个具体的TransactionFormatter类型:OleTxTransactionFormatter、WsatTransactionFormatter10和WsatTransactionFormatter11,它们分别对应于WCF支持的三种事务处理协议:OleTx、WS-AT 1.0和WS-AT 1.1。

我想很多人很想知道一个Transaction对象被不同的TransactionFormatter写入到Message对象后,Message具有怎样的格式呢?接下来我们通过一个简单的实例来演示。

三、实例演示:通过TransactionFormatter进行事务的写入

本实例是一个简单的控制台应用,我们将用它来演示模拟事务绑定是如何将当前事务写入消息。由于上面提到的TransactionFormatter和TransactionInfo都是内部类型,我们只能通过反射的方式使用它们。为此,我写了一个简单的工具类型ReflectUtil,用于通过反射的方式创建对象和调用某个方法,原理很简单,在这里就不多作介绍了。

   1: internal static class ReflectUtil
   2: {
   3:     public static object CreateInstance(string typeAssemblyQName, params object[] parameters)
   4:     {
   5:         Type typeofInstance = Type.GetType(typeAssemblyQName);
   6:         BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
   7:         return Activator.CreateInstance(typeofInstance, bindingFlags, null, parameters, null);
   8:     }
   9:     public static object Invoke(string methodName, object targetInstance, params object[] parameters)
  10:     {
  11:         BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public;
  12:         return targetInstance.GetType().GetMethod(methodName, bindingFlags).Invoke(targetInstance, parameters);
  13:     }
  14: }

然后我们创建我们自己的TransactionFormatter类型,它具有相同的方法ReadTransaction和WriteTransaction,ReadTransaction的返回类型直接使Transaction。相应的事务协议通过构造函数指定,事务协议决定了最终创建的真正TransactionFormatter的类型。真正的TransactionFormatter通过ReflectUtil创建,相应的方法也通过ReflectUtil调用。

   1: public class TransactionFormatter
   2: {
   3:     const string OleTxFormatterType = "System.ServiceModel.Transactions.OleTxTransactionFormatter,System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089";
   4:     const string Wsat10FormatterType = "System.ServiceModel.Transactions.WsatTransactionFormatter10,System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089";
   5: const string Wsat11FormatterType = "System.ServiceModel.Transactions.WsatTransactionFormatter11,System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089";
   6:     
   7:     public object InternalFormatter
   8: { get; private set; }
   9:  
  10:     public TransactionFormatter(TransactionProtocol transactionProtocol)
  11:     {
  12:         if (transactionProtocol == TransactionProtocol.OleTransactions)
  13:         {
  14:             this.InternalFormatter = ReflectUtil.CreateInstance(OleTxFormatterType);
  15:         }
  16:         else if (transactionProtocol == TransactionProtocol.WSAtomicTransactionOctober2004)
  17:         {
  18:             this.InternalFormatter = ReflectUtil.CreateInstance(Wsat10FormatterType);
  19:         }
  20:         else
  21:         {
  22:             this.InternalFormatter = ReflectUtil.CreateInstance(Wsat11FormatterType);
  23:         }
  24:     }
  25:  
  26:     public Transaction ReadTransaction(Message message)
  27:     {
  28:         object transInfo = ReflectUtil.Invoke("ReadTransaction", this.InternalFormatter, message);
  29:         return ReflectUtil.Invoke("UnmarshalTransaction", transInfo) as Transaction;
  30:     }
  31:  
  32:     public void WriteTransaction(Transaction transaction, Message message)
  33:     {
  34:         ReflectUtil.Invoke("WriteTransaction", this.InternalFormatter, transaction, message);
  35:     }
  36: }

然后我们基于三种不同的事务处理协议创建了相应的TransactionFormatter对象,并将相同的Transaction对象写入到一个Message对象中,并且Message的主体部分为Transaction对象本身。最终的Message对象被写入到3个XML文文件中。


   1: static void Main(string[] args)
   2: {
   3:     using (TransactionScope transactionScope = new TransactionScope())
   4:     {
   5:         WriteTransaction(TransactionProtocol.OleTransactions, Transaction.Current, "oletx.xml");
   6:         WriteTransaction(TransactionProtocol.WSAtomicTransactionOctober2004, Transaction.Current, "wsat10.xml");
   7:         WriteTransaction(TransactionProtocol.WSAtomicTransaction11, Transaction.Current, "wsat11.xml");
   8:     }
   9: }
  10:  
  11: static void WriteTransaction(TransactionProtocol transactionProtocol, Transaction transaction,  string fileName)
  12: {
  13:     string action = string.Format("http://www.artech.com/transactionformat/{0}", transactionProtocol.GetType().Name);
  14:     Message message = Message.CreateMessage(MessageVersion.Default, action, Transaction.Current);
  15:     TransactionFormatter formatter = new TransactionFormatter(transactionProtocol);
  16:     formatter.WriteTransaction(Transaction.Current, message);
  17:     using (XmlWriter writer = new XmlTextWriter(fileName, Encoding.UTF8))
  18:     {
  19:         message.WriteMessage(writer);
  20:     }
  21:     Process.Start(fileName);
  22: }

程序成功运行后,你将会得到三个表示Message对象的XML文件,它们的内容如下。有兴趣的读者可以结合相应事务处理协议规范,认真分析一下对应消息的结构,相信可以加深你对事务处理协议的理解。

OleTx.xml(OleTx)

   1: <s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">
   2:   <s:Header>
   3:     <a:Action s:mustUnderstand="1">http://www.artech.com/transactionformat/OleTransactionsProtocol</a:Action>
   4:     <OleTxTransaction s:mustUnderstand="1" d3p1:Expires="59392" xmlns:d3p1="http://schemas.xmlsoap.org/ws/2004/10/wscoor" xmlns="http://schemas.microsoft.com/ws/2006/02/tx/oletx">
   5:       <PropagationToken>AQAAAAMAAADknHb/v0zMQKhDZHfsF0Y1AAAQAAAAAACEAAAAAMW2bRzFtm00W6xnBOMYAHfXuW00W6xnTOQYADi1JADAu1YAkOMYADg5YjE0NzZkLTE2ZmYtNGI4MS05ZjEwLTE5MDE3ZTkwYjU1MgBiWWwMAAAAZM1kzSEAAABKSU5OQU4tVklTVEEAAAAAHAAAAEoASQBOAE4AQQBOAC0AVgBJAFMAVABBAAAAAAABAAAAAAAAABMAAAB0aXA6Ly9KaW5uYW4tVmlzdGEvAA==</PropagationToken>
   6:     </OleTxTransaction>
   7:   </s:Header>
   8:   <s:Body>
   9:     <Transaction xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:x="http://www.w3.org/2001/XMLSchema" xmlns:d3p3="http://schemas.datacontract.org/2004/07/System.Transactions.Oletx" z:FactoryType="d3p3:OletxTransaction" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" xmlns="http://schemas.datacontract.org/2004/07/System.Transactions">
  10:       <OletxTransactionPropagationToken i:type="x:base64Binary" xmlns="">AQAAAAMAAADknHb/v0zMQKhDZHfsF0Y1AAAQAAAAAACEAAAAAMW2bRzFtm00W6xnBOMYAHfXuW00W6xnTOQYADi1JADAu1YAkOMYADg5YjE0NzZkLTE2ZmYtNGI4MS05ZjEwLTE5MDE3ZTkwYjU1MgDRiGgMAAAAZM1kzSEAAABKSU5OQU4tVklTVEEAAAAAHAAAAEoASQBOAE4AQQBOAC0AVgBJAFMAVABBAAAAAAABAAAAAAAAABMAAAB0aXA6Ly9KaW5uYW4tVmlzdGEvAA==</OletxTransactionPropagationToken>
  11:     </Transaction>
  12:   </s:Body>
  13: </s:Envelope>

Wsat10.xml(WS-AT 1.0):

   1: <s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">
   2:   <s:Header>
   3:     <a:Action s:mustUnderstand="1">http://www.artech.com/transactionformat/ WSAtomicTransactionOctober2004Protocol</a:Action>
   4:     <CoordinationContext s:mustUnderstand="1" xmlns:mstx="http://schemas.microsoft.com/ws/2006/02/transactions" xmlns="http://schemas.xmlsoap.org/ws/2004/10/wscoor">
   5:       <wscoor:Identifier xmlns:wscoor="http://schemas.xmlsoap.org/ws/2004/10/wscoor">urn:uuid:ff769ce4-4cbf-40cc-a843-6477ec174635</wscoor:Identifier>
   6:       <Expires>59392</Expires>
   7:       <CoordinationType>http://schemas.xmlsoap.org/ws/2004/10/wsat</CoordinationType>
   8:       <RegistrationService>
   9:         <Address xmlns="http://schemas.xmlsoap.org/ws/2004/08/addressing">https://jinnan-vista/WsatService/Registration/Coordinator/ /</Address>
  10:         <ReferenceParameters xmlns="http://schemas.xmlsoap.org/ws/2004/08/addressing">
  11:           <mstx:RegisterInfo>
  12:             <mstx:LocalTransactionId>ff769ce4-4cbf-40cc-a843-6477ec174635</mstx:LocalTransactionId>
  13:           </mstx:RegisterInfo>
  14:         </ReferenceParameters>
  15:       </RegistrationService>
  16:       <mstx:IsolationLevel>0</mstx:IsolationLevel>
  17:       <mstx:LocalTransactionId>ff769ce4-4cbf-40cc-a843-6477ec174635</mstx:LocalTransactionId>
  18:       <PropagationToken xmlns="http://schemas.microsoft.com/ws/2006/02/tx/oletx">AQAAAAMAAADknHb/v0zMQKhDZHfsF0Y1AAAQAAAAAACEAAAAAMW2bRzFtm00W6xnBOMYAHfXuW00W6xnTOQYADi1JADAu1YAkOMYADg5YjE0NzZkLTE2ZmYtNGI4MS05ZjEwLTE5MDE3ZTkwYjU1MgDtiGgMAAAAZM1kzSEAAABKSU5OQU4tVklTVEEAAAAAHAAAAEoASQBOAE4AQQBOAC0AVgBJAFMAVABBAAAAAAABAAAAAAAAABMAAAB0aXA6Ly9KaW5uYW4tVmlzdGEvAA==</PropagationToken>
  19:     </CoordinationContext>
  20:   </s:Header>
  21:   <s:Body>
  22:     <Transaction xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:x="http://www.w3.org/2001/XMLSchema" xmlns:d3p3="http://schemas.datacontract.org/2004/07/System.Transactions.Oletx" z:FactoryType="d3p3:OletxTransaction" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" xmlns="http://schemas.datacontract.org/2004/07/System.Transactions">
  23:       <OletxTransactionPropagationToken i:type="x:base64Binary" xmlns="">AQAAAAMAAADknHb/v0zMQKhDZHfsF0Y1AAAQAAAAAACEAAAAAMW2bRzFtm00W6xnBOMYAHfXuW00W6xnTOQYADi1JADAu1YAkOMYADg5YjE0NzZkLTE2ZmYtNGI4MS05ZjEwLTE5MDE3ZTkwYjU1MgAAAAAMAAAAZM1kzSEAAABKSU5OQU4tVklTVEEAJVwAHAAAAEoASQBOAE4AQQBOAC0AVgBJAFMAVABBAAAA//8BAAAAAAAAABMAAAB0aXA6Ly9KaW5uYW4tVmlzdGEvAA==</OletxTransactionPropagationToken>
  24:     </Transaction>
  25:   </s:Body>
  26: </s:Envelope>

Wsat11.xml(WS-AT 1.1)

   1: <s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">
   2:   <s:Header>
   3:     <a:Action s:mustUnderstand="1">http://www.artech.com/transactionformat/WSAtomicTransaction11Protocol</a:Action>
   4:     <CoordinationContext s:mustUnderstand="1" xmlns:mstx="http://schemas.microsoft.com/ws/2006/02/transactions" xmlns="http://docs.oasis-open.org/ws-tx/wscoor/2006/06">
   5:       <wscoor:Identifier xmlns:wscoor="http://docs.oasis-open.org/ws-tx/wscoor/2006/06">urn:uuid:ff769ce4-4cbf-40cc-a843-6477ec174635</wscoor:Identifier>
   6:       <Expires>59392</Expires>
   7:       <CoordinationType>http://docs.oasis-open.org/ws-tx/wsat/2006/06</CoordinationType>
   8:       <RegistrationService>
   9:         <a:Address>https://jinnan-vista/WsatService/Registration/Coordinator11</a:Address>
  10:         <a:ReferenceParameters>
  11:           <mstx:RegisterInfo>
  12:             <mstx:LocalTransactionId>ff769ce4-4cbf-40cc-a843-6477ec174635</mstx:LocalTransactionId>
  13:           </mstx:RegisterInfo>
  14:         </a:ReferenceParameters>
  15:       </RegistrationService>
  16:       <mstx:IsolationLevel>0</mstx:IsolationLevel>
  17:       <mstx:LocalTransactionId>ff769ce4-4cbf-40cc-a843-6477ec174635</mstx:LocalTransactionId>
  18:       <PropagationToken xmlns="http://schemas.microsoft.com/ws/2006/02/tx/oletx">AQAAAAMAAADknHb/v0zMQKhDZHfsF0Y1AAAQAAAAAACEAAAAAMW2bRzFtm00W6xnBOMYAHfXuW00W6xnTOQYADi1JADAu1YAkOMYADg5YjE0NzZkLTE2ZmYtNGI4MS05ZjEwLTE5MDE3ZTkwYjU1MgAAMAAMAAAAZM1kzSEAAABKSU5OQU4tVklTVEEAAGwAHAAAAEoASQBOAE4AQQBOAC0AVgBJAFMAVABBAAAAbgABAAAAAAAAABMAAAB0aXA6Ly9KaW5uYW4tVmlzdGEvAA==</PropagationToken>
  19:     </CoordinationContext>
  20:   </s:Header>
  21:   <s:Body>
  22:     <Transaction xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:x="http://www.w3.org/2001/XMLSchema" xmlns:d3p3="http://schemas.datacontract.org/2004/07/System.Transactions.Oletx" z:FactoryType="d3p3:OletxTransaction" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" xmlns="http://schemas.datacontract.org/2004/07/System.Transactions">
  23:       <OletxTransactionPropagationToken i:type="x:base64Binary" xmlns="">AQAAAAMAAADknHb/v0zMQKhDZHfsF0Y1AAAQAAAAAACEAAAAAMW2bRzFtm00W6xnBOMYAHfXuW00W6xnTOQYADi1JADAu1YAkOMYADg5YjE0NzZkLTE2ZmYtNGI4MS05ZjEwLTE5MDE3ZTkwYjU1MgDRiGgMAAAAZM1kzSEAAABKSU5OQU4tVklTVEEAAAAAHAAAAEoASQBOAE4AQQBOAC0AVgBJAFMAVABBAAAAAAABAAAAAAAAABMAAAB0aXA6Ly9KaW5uYW4tVmlzdGEvAA==</OletxTransactionPropagationToken>
  24:     </Transaction>
  25:   </s:Body>
  26: </s:Envelope>
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2010-03-17 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、TransactionFlowAttribute:将TransactionFlow选项存入绑定上下文(BindingContext)
  • 二、 事务绑定:实现事务的流转
  • 三、实例演示:通过TransactionFormatter进行事务的写入
相关产品与服务
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档