WCF技术剖析之三十三:你是否了解WCF事务框架体系内部的工作机制?[上篇]
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所示。
图1 事务流转相关的绑定元素、信道管理器和信道结构
客户端的事务信道需要将当前事务写入消息,而服务端的事务信道则需要将流入的事务从服务中读出来。WCF将事务的读写操作定义在一个称为TransactionFormatter的类型中。不过,这是一个内部(Internal)类型不能直接使用。TransactionFormatter的定义大体上如下面的代码所示,其中ReadTransaction和WriteTransaction分别实现对事务的读取和写入操作。事务通过TransactionInfo对象的形式被读取出来,TransactionInfo是也是一个内部对象,我们可以通过调用UnmarshalTransaction得到真正的Transaction对象。
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,用于通过反射的方式创建对象和调用某个方法,原理很简单,在这里就不多作介绍了。
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调用。
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文文件中。
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
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
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
微信公众账号:大内老A
微博:www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。 原文链接
一、WCF技术我该如何学习? 阿笨的回答是:作为初学者的我们,那么请跟着阿笨一起玩WCF吧,阿笨将带领大家如何以正确的姿势去掌握WCF技术。由于WCF技术知识点太多了,就纯基础概念性知识都可以单独出一本书来讲解,本次分享课程《C#面向服务编程技术WCF从入门到实战演练》开课之前,阿笨还是希望从没了解过WCF技术的童鞋们提前先了解一下WCF技术,至少要明白WCF技术的ABC三要素分别指的是什么。
相关文章
- WCF安全1-开篇
- [WCF-Discovery]让服务自动发送上/下线通知[原理篇]
- [WCF 4.0新特性] 默认绑定和行为配置
- [WCF安全系列]通过绑定元素看各种绑定对消息保护的实现
- [WCF安全系列]从两种安全模式谈起
- 使命必达: 深入剖析WCF的可靠会话[共8篇]
- WCF技术剖析之三十一: WCF事务编程[中篇]
- WCF技术剖析之二十七: 如何将一个服务发布成WSDL[基于WS-MEX的实现](提供模拟程序)
- WCF技术剖析之十一:异步操作在WCF中的应用(下篇)
- WCF技术剖析之九:服务代理不能得到及时关闭会有什么后果?
- [WCF的Binding模型]之三:信道监听器(Channel Listener)