原文:WCF技术剖析之十七:消息(Message)详解(中篇)

[爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道《天天山海经》为此录制的节目视频(苏州话)]]在上篇中大体上围绕着Message的两个话题进行讲述:消息版本(Message Version)和采用五种不同的方式创建Message。本篇文章将会详细介绍Message的另外两个主题:和消息的基本操作,比如读、写、拷贝、关闭等,以及消息状态机(Message State Machine)。

知道了消息是如何创建的,我们接着讨论消息的一些基本的操作。除了上面介绍的消息创建之外,一个消息涉及到的操作大体分为以下4类:

  • 读消息:读取整个消息的内容或者有选择地读取报头或者主体部分内容;
  • 写消息:将整个消息的内容或者主体部分内容写入文件或者流;
  • 拷贝消息:通过消息拷贝生成另一个具有相同内容的新消息;
  • 关闭消息:关闭消息,回收一些非托管资源。

上述的这些消息的基本操作都和消息的状态密切相关,消息操作和消息状态之间的关系体现在以下两个方面:

  • 消息的状态决定了可以采取的操作;
  • 消息操作伴随着消息状态的改变。

一、消息状态机(Message State Machine)

图1展示的是基于消息的状态机,从图中我们可以得出下面的一些关于Message对象状态转换的规则:

  • 消息的读、写和拷贝操作只能作用于状态为Created的消息上;
  • 消息的读、写和拷贝将消息状态从Created转换成Read、Written和Copied。
  • 所有状态的消息都可以直接关闭,关闭后消息的状态转换为Closed。

图1 Message对象状态机

在WCF中,消息的状态通过System.ServiceModel.Channels.MessageState枚举表示,MessageState定义了5种消息状态,与上图所示的5种状态一一对应。MessageState的定义如下:

 1: public enum MessageState

 2: {

 3: Created,

 4: Read,

 5: Written,

 6: Copied,

 7: Closed

 8: }

二、消息的读取

读取消息主体部分的内容是最为常见的操作。如果主体部分的内容对应一个可以序列化的对象,可以通过GetBody<T>方法读取消息主体并反序列化生成相应的对象。而通过GetReaderAtBodyContents得到一个XmlDictionaryReader对象,通过这个对象可以进一步提取消息主体部分的内容。

 1: public abstract class Message : IDisposable

 2: {

 3: //其他成员

 4: public T GetBody<T>();

 5: public T GetBody<T>(XmlObjectSerializer serializer);

 6: public XmlDictionaryReader GetReaderAtBodyContents(); 

 7: }

我们演示一下GetBody<T>方法的例子。假设消息主体部分对应的类型为下面所示的Customer类,这是一个数据契约。

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

 2: public class Customer

 3: {

 4: [DataMember]

 5: public string Name

 6: { get; set; }

 7:  

 8: [DataMember]

 9: public string Compnay

 10: { get; set; }

 11:  

 12: [DataMember]

 13: public string Address

 14: { get; set; }

 15:  

 16: public override bool Equals(object obj)

 17: {

 18: Customer customer = obj as Customer;

 19: if (customer == null)

 20: {

 21: return false;

 22: }

 23: return this.Name == customer.Name && this.Compnay == customer.Compnay && this.Address == customer.Address;

 24: }

 25: }

在下面的程序中,通过Customer对象创建Message对象,调用GetBody<Customer>方法读取主体部分的内容并反序列化成Customer对象。可以想象开始创建的Customer对象和通过GetBody<Customer>方法得到的Customer对象的是得相等的,输出的结果证明了这一点。

 1: Customer customer = new Customer

 2: {

 3: Name = "Foo",

 4: Compnay = "NCS",

 5: Address = "#328, Airport Rd, Industrial Park, Suzhu Jiangsu Province"

 6: };

 7: Message message = Message.CreateMessage(MessageVersion.Default, "http://www.artech.com/myaction", customer);

 8: Customer cusomterToRead = message.GetBody<Customer>();

 9: Console.WriteLine("customer.Equals(cusomterToRead) = {0}", customer.Equals(cusomterToRead));

输出结果:

 1: customer.Equals(cusomterToRead) = True

按照我们上面介绍的消息状态机所描述的,只有状态为Created的消息才能执行读取操作,否则会抛出异常。无论是执行了GetBody<T>方法还是GetReaderAtBodyContents方法,Message对象的状态都将转换为Read。在上面代码的基础上,添加了两行额外的代码输出消息的状态,并再一次调用Message对象的GetBody<T>方法。程序运行输出消息的状态(message.State = Read),正执行到第2个GetBody<T>方法时,抛出如图2所示的InvalidOperationException异常。

 1: //省略代码

 2: Message message = Message.CreateMessage(MessageVersion.Default, "http://www.artech.com/myaction", customer);

 3: Customer cusomterToRead = message.GetBody<Customer>();

 4: Console.WriteLine("message.State = {0}", message.State);

 5: cusomterToRead = message.GetBody<Customer>();

 6: Console.WriteLine("customer.Equals(cusomterToRead) = {0}", customer.Equals(cusomterToRead));

输出结果:

 1: message.State = Read

图2 重复读取消息导致的异常

三、消息的写入

在Message类中,定义了一系列WriterXxx方法用于消息的写操作。通过这些方法,我们可以将整个消息或者是消息的主体部分内容写入XmlWriter或者XmlDictioanryWriter中,最终写入文件或者流。

 1: public abstract class Message : IDisposable

 2: { 

 3: //其他成员

 4: public void WriteBody(XmlDictionaryWriter writer);

 5: public void WriteBody(XmlWriter writer);

 6: public void WriteBodyContents(XmlDictionaryWriter writer);

 7: public void WriteMessage(XmlDictionaryWriter writer);

 8: public void WriteMessage(XmlWriter writer);

 9: public void WriteStartBody(XmlDictionaryWriter writer);

 10: public void WriteStartBody(XmlWriter writer);

 11: public void WriteStartEnvelope(XmlDictionaryWriter writer);

 12: }

我们在前面作演示时创建的辅助方法WriteMessage(如下面的代码所示),就是通过调用WriteMessage方法将消息的内容写入一个指定的XML文件中的。同消息的读取一样,写操作只能作用于状态为Created的消息。成功执行了消息写入操作后,状态转换为Written。

 1: static void WriteMessage(Message message, string fileName)

 2: {

 3: using (XmlWriter writer = new XmlTextWriter(fileName, Encoding.UTF8))

 4: {

 5: message.WriteMessage(writer);

 6: }

 7: Process.Start(fileName);

 8: }

四、消息的拷贝

通过前面的介绍和演示,相信读者对消息的状态转换已有一个清晰的认识:消息的读写都会改变消息的状态,而读写操作只能作用于状态为Created的消息。由此就出现了这样一个问题:在真正的WCF应用中,我们往往需要将消息进行日志记录。如果按照正常的方式进行消息的读取和写入,会导致状态的改变,如果消息传递到WCF的处理管道,作用于该消息对象的读、写操作都将失败。在这种情况下,我们需要使用到消息的拷贝功能。Message类中定义了一个CreateBufferedCopy方法,专门用于消息的拷贝。

 1: public abstract class Message : IDisposable

 2: {

 3: //其他成员

 4: public MessageBuffer CreateBufferedCopy(int maxBufferSize);

 5: }

CreateBufferedCopy方法的返回结果并不是我们想象的Message对象,而是一个System.ServiceModel.Channels.MessageBuffer对象,MessageBuffer表示消息在内存中缓存。当CreateBufferedCopy成功执行后,消息的状态转换成Copied,很显然后续的操作不能再使用该消息。但是却可以通过MessageBuffer对象创建一个新的Message对象,该对象具有与原来一样的内容,但是状态却是Created。在MessageBuffer中,定义了如下一个CreateMessage方法,用于新消息的创建。

 1: public abstract class MessageBuffer : IXPathNavigable, IDisposable

 2: {

 3: //其他成员

 4: public abstract Message CreateMessage();

 5: }

比如,我们通过下面的方式解决前面所演示的重复读取的问题,将不会在有InvalidOperatioException异常抛出。

 1: Message message = Message.CreateMessage(MessageVersion.Default, "http://www.artech.com/myaction", customer);

 2: MessageBuffer messageBuffer = message.CreateBufferedCopy(int.MaxValue);

 3: message = messageBuffer.CreateMessage(); 

 4: Customer cusomterToRead = message.GetBody<Customer>();

 5: message = messageBuffer.CreateMessage();

 6: cusomterToRead = message.GetBody<Customer>();

 7: Console.WriteLine("customer.Equals(cusomterToRead) = {0}", 

作者:Artech
出处:http://artech.cnblogs.com

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

WCF技术剖析之十七:消息(Message)详解(中篇)的更多相关文章

  1. WCF技术剖析之十七:消息(Message)详解(下篇)

    原文:WCF技术剖析之十七:消息(Message)详解(下篇) [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道<天天山海经>为此录制的节目视频(苏州话)]]< ...

  2. WCF技术剖析之十七:消息(Message)详解(上篇)

    原文:WCF技术剖析之十七:消息(Message)详解(上篇) [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道<天天山海经>为此录制的节目视频(苏州话)]]消息交换 ...

  3. WCF技术剖析之十八:消息契约(Message Contract)和基于消息契约的序列化

    原文:WCF技术剖析之十八:消息契约(Message Contract)和基于消息契约的序列化 [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道<天天山海经>为此录制 ...

  4. WCF技术剖析之二十七: 如何将一个服务发布成WSDL[基于HTTP-GET的实现](提供模拟程序)

    原文:WCF技术剖析之二十七: 如何将一个服务发布成WSDL[基于HTTP-GET的实现](提供模拟程序) 基于HTTP-GET的元数据发布方式与基于WS-MEX原理类似,但是ServiceMetad ...

  5. WCF技术剖析之二十七: 如何将一个服务发布成WSDL[基于WS-MEX的实现](提供模拟程序)

    原文:WCF技术剖析之二十七: 如何将一个服务发布成WSDL[基于WS-MEX的实现](提供模拟程序) 通过<如何将一个服务发布成WSDL[编程篇]>的介绍我们知道了如何可以通过编程或者配 ...

  6. WCF技术剖析之二十七: 如何将一个服务发布成WSDL[编程篇]

    原文:WCF技术剖析之二十七: 如何将一个服务发布成WSDL[编程篇] 对于WCF服务端元数据架构体系来说,通过MetadataExporter将服务的终结点导出成MetadataSet(参考< ...

  7. WCF技术剖析之十九:深度剖析消息编码(Encoding)实现(下篇)

    原文:WCF技术剖析之十九:深度剖析消息编码(Encoding)实现(下篇) [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道<天天山海经>为此录制的节目视频(苏州话 ...

  8. 《WCF技术剖析》博文系列汇总[持续更新中]

    原文:<WCF技术剖析>博文系列汇总[持续更新中] 近半年以来,一直忙于我的第一本WCF专著<WCF技术剖析(卷1)>的写作,一直无暇管理自己的Blog.在<WCF技术剖 ...

  9. WCF技术剖析之二十八:自己动手获取元数据[附源代码下载]

    原文:WCF技术剖析之二十八:自己动手获取元数据[附源代码下载] 元数据的发布方式决定了元数据的获取行为,WCF服务元数据架构体系通过ServiceMetadataBehavior实现了基于WS-ME ...

随机推荐

  1. Python3 将configparser从ini文件中读取的内容转换成字典格式

    因为写脚本的用到了,所以研究了下怎么将configparser从ini文件中读取的内容转换成字典格式. 整理一下,希望能对大家有帮助. 从http://stackoverflow.com/questi ...

  2. [LeetCode]题解(python):012-Integer to Roman

    题目来源: https://leetcode.com/problems/integer-to-roman/ 题意分析: 这道题是要把在区间[1-3999]的数字转化成罗马数字. 题目思路: 只要知道了 ...

  3. Recursive Depth first search graph(adj matrix)

    1 深度优先遍历邻接矩阵 1 邻接矩阵初始化 2 访问数组初始化 3 深度优先遍历邻接矩阵图 算法如下: bool MGraph[128][128]; bool visit[128]; int vex ...

  4. c# .net 读取json 字符串 与序列化和反序列化json字符串

    命名空间 using Newtonsoft.Json.Linq; JObject obj = JObject.Parse("json字符串");用 obj["" ...

  5. randn命令中randn('state')和randn('seed')的不同

     (1)RANDN产生正态分布数的语法: RANDN(N) :产生N× N的矩阵,其元素是按正态分布的数组: RANDN(M,N) and RANDN([M,N]):产生M×N的矩阵: RANDN ...

  6. [转]组合数取模 Lucas定理

    对于C(n, m) mod p.这里的n,m,p(p为素数)都很大的情况.就不能再用C(n, m) = C(n - 1,m) + C(n - 1, m - 1)的公式递推了. 这里用到Lusac定理 ...

  7. 五张图概括 什么是 ASP 、 ASP.NET (Web Pages,Web Forms ,MVC )

    当你看懂下面这五张图,我相信你对于学习.NET Web开发路线将不陌生!                                               来源: http://www.w3 ...

  8. 网络编程(UDP协议-聊天程序)

    网络编程中的UDP协议中聊天程序,发送端口,和接受端口. 发送端口(Send): <span style="font-size:18px;">package cn.it ...

  9. windows 下搭建 apache + php52 + postgreSQL7/8/9环境

    apache和php安装参考:[转]Windows7 64bit下配置Apache+PHP+MySQL 我这主要讲配置  apache 支持 postgresql9数据库: 1.将php5文件夹下的p ...

  10. svn密码问题

    官方书籍version control with svn提到了这个问题: Disabling Password Caching When you perform a Subversion operat ...