WCF技术内幕之消息

Message类型介绍

    Message引用类型是WCF应用对SOAP消息的抽象。由于与SOAP消息如此紧密,因此Message类型定义了表示SOAP的版本、信封、消息头、消息头块和消息体元素的成员。从功能上讲,Message类型实际是对数据存储的一个包装,并且这个数据存储就是XML Infoset。

    在整个生命周期里,传输到其他消息参与者之前,Message对象必须经过几次转换。从发送者角度来看,这个转换包含两步:序列化和编码。Message序列化是把Message实例化为XML Infoset,编码是把XML Infoset转换为特定的数据格式。从接收者角度来看,这个转换正好与发送者相反。

WCF XML栈

    有三个关键的类型定义负责序列化和编码Message类型:XmlDictionary、XmlDictionaryWrite和XmlDictionaryReader。

        XmlDictionary类型XmlDictionary对象是许多对key-value的映射。如字典和词汇表一样,XmlDictionary可以用简单的词语表示复杂的东西,并且不会丢失任何含义。在消息应用里,XmlDictionary有可能用来压缩经过序列化和编码的消息大小和编码过的消息大小,因此,可以减少需要发送消息占用的带宽。从内部来看,XmlDictionary定义了一个私有的可以表示SOAP消息中元素名字、属性和XML namespace声明的key-value对列表。XmlDictionary内部存储的key-value对是XmlDictionaryString类型的。XmlDictionaryString可以通过调用实例方法Add加入XmlDictionary里。Add方法接受一个String类型,返回一个XmlDictionaryString实例,如下所示:

        XmlDictionary dictionary = new XmlDictionary();
        List stringList = new List();
        //增加元素名称到字典里,然后存储在StringList中
        stringList.Add(dictionary.Add("ReleaseDate"));
        stringList.Add(dictionary.Add("GoodSongs"));
        stringList.Add(dictionary.Add("Studio"));
        Console.WriteLine("entries in Collection:");
        foreach(XmlDictionaryString entry in stringList)
        {
            Console.WriteLine("Key={0},Value={1}", entry.Key, entry.Value);
        }
        Console.ReadKey();

当执行前面代码时,可以看到Key属性的值自动被赋予每个XmlDictionaryString。

    XmlDictionaryWrite类型

        XmlDictionaryWrite类型是用来序列化和编码Message类型的,并且有时会使用XmlDictionary对象处理压缩工作。它继承自System.Xml.XmlWriter,因此继承了很多XmlWrite的属性。它定义了几个返回XmlDictionaryWrite子类型实例的工厂方法,包装了System.IO.Stream并定义了许多以Write单词开始的方法。这些方法大部分重载以下4个方法:CreateDictionaryWriter、CreateTextWriter、CreateMtomWriter和CreateBinaryWriter。

        XmlDictionaryWriter类型上的工厂方法CreateDictionaryWrite就是接受XmlWrite类型的引用。内部来说,这些方法返回的实例都是简单地包装了传递的参数XmlWriter。

        XmlDictionaryWriter类型定义了三种创建CreateTextWriter的工厂方法。这些工厂方法返回的是继承自XmlDictionaryWrite类型的实例,并且它们的作用是为了产生基于文本编码的XML。以下代码演示了如何使用CreateTextWriter方法:

        MemoryStream stream = new MemoryStream();
        //XmlDictionaryWrite 放在using结构里,因此可以确保Dispose方法被调用。
        using (XmlDictionaryWriter writer = XmlDictionaryWriter.CreateTextWriter(stream, Encoding.UTF8, false))
        {
            writer.WriteStartDocument();
            writer.WriteElementString("SongName","urn:ContosoRockabilia","Aqualung");
            writer.Flush();
        }
        Console.WriteLine("XmlDictionaryWrite(Text-UTF8 wrote {0} bytes)",stream.Position);
        stream.Position = 0;
        Byte[] bytes = stream.ToArray();
        Console.WriteLine(BitConverter.ToString(bytes));
        //输出流中文本
        Console.WriteLine("data read from stream:\n{0}\n", new StreamReader(stream).ReadToEnd());

当执行程序代码时,它会产生以下结果。

WCF技术内幕之消息_第1张图片

            XmlDictionaryWrite定义了两种CreateMtomWrite方法。这些方法返回的是继承自XmlDictionaryWrite并产生MTOM编码的XML实例。两者都接收Stream类型的参数和几个别的控制XML Infoset编码的参数。这些参数设置编码格式、SOAP消息头的ContentType、多用途网络邮件扩展协议(MIME)的边界和MIME的统一资源标识符(URI),同时也包括是否把消息写入Stream对象。

                XmlDictionaryWrite也定义了四种CreateBinaryWriter方法。这些方法返回的是继承自XmlDictionaryWriter并能产生二进制编码的XML实例。CreateBinaryWrite方法上的XmlBinaryWriterSession参数允许发送者和接收者自动采集和协调一个动态的XmlDictionary。为了在数据接收结束之后解码数据,接收者必须使用XmlBinaryReaderSession对象。XmlBinaryReaderSession对象会根据之前接收到的Steam对象中的dictionaey来自动组装自己。

        XmlDictionaryReader类型

            XmlDictionaryReader抽象类型继承自System.Xml.XMlReader,因此继承了很多XmlReader的特性。XmlDictionaryReader的工厂方法接收的参数和XmlDictionaryWriter的工厂方法几乎一一对应。这些参数与在XmlDictionaryWriter类型里的作用一样。

创建消息

    可以选择众多CreateMessage工厂方法中来创建Message对象。这些方法绝大部分接收的都以SOAP消息体的内容作为参数。非常重要的一点是,Message的body在创建以后不能在修改。而SOAP消息头块在消息创建以后还可以增加和修改。

    简要介绍Message序列化和反序列化

        当发送程序需要发送一个Message到另一个消息参与者时,它必须创建包含适当信息的Message对象,然后序列化和编码Message到Stream或Byte状态(和发送者最后处理的数据状态一样)。接收程序必须解码和反序列化Stream 或Byte[]为Message对象,或许还要反序列化消息头部或消息体为其他对象。

    Message版本

        因为Message对象是CLR对SOAP消息的抽象,而使用的SOAP消息有多种版本,所以需要考虑Message对象所示的SOAP消息版本问题。当设置Message对象时,System.ServiceModel.Channels.EncelopeVersion类型代表了Message类型遵循的SOAP规范。同时,System.ServiceModel.Channels.AddressingVersion表示Message序列化时,消息头块遵循WS-Addressing规范。以下代码显示了MessageVersion的所有公开可见的成员:

// System.ServiceModel.Channels.MessageVersion
using System;
using System.ComponentModel;
using System.Globalization;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;

[TypeConverter(typeof(MessageVersionConverter))]
[__DynamicallyInvokable]
public sealed class MessageVersion
{
	private EnvelopeVersion envelope;

	private AddressingVersion addressing;

	private static MessageVersion none;

	private static MessageVersion soap11;

	private static MessageVersion soap12;

	private static MessageVersion soap11Addressing10;

	private static MessageVersion soap12Addressing10;

	private static MessageVersion soap11Addressing200408;

	private static MessageVersion soap12Addressing200408;

	[__DynamicallyInvokable]
	public AddressingVersion Addressing
	{
		[__DynamicallyInvokable]
		get
		{
			return this.addressing;
		}
	}

	[__DynamicallyInvokable]
	public static MessageVersion Default
	{
		[__DynamicallyInvokable]
		get
		{
			return MessageVersion.soap12Addressing10;
		}
	}

	[__DynamicallyInvokable]
	public EnvelopeVersion Envelope
	{
		[__DynamicallyInvokable]
		get
		{
			return this.envelope;
		}
	}

	[__DynamicallyInvokable]
	public static MessageVersion None
	{
		[__DynamicallyInvokable]
		get
		{
			return MessageVersion.none;
		}
	}

	[__DynamicallyInvokable]
	public static MessageVersion Soap12WSAddressing10
	{
		[__DynamicallyInvokable]
		get
		{
			return MessageVersion.soap12Addressing10;
		}
	}

	public static MessageVersion Soap11WSAddressing10
	{
		get
		{
			return MessageVersion.soap11Addressing10;
		}
	}

	public static MessageVersion Soap12WSAddressingAugust2004
	{
		get
		{
			return MessageVersion.soap12Addressing200408;
		}
	}

	public static MessageVersion Soap11WSAddressingAugust2004
	{
		get
		{
			return MessageVersion.soap11Addressing200408;
		}
	}

	[__DynamicallyInvokable]
	public static MessageVersion Soap11
	{
		[__DynamicallyInvokable]
		get
		{
			return MessageVersion.soap11;
		}
	}

	public static MessageVersion Soap12
	{
		get
		{
			return MessageVersion.soap12;
		}
	}

	static MessageVersion()
	{
		MessageVersion.none = new MessageVersion(EnvelopeVersion.None, AddressingVersion.None);
		MessageVersion.soap11 = new MessageVersion(EnvelopeVersion.Soap11, AddressingVersion.None);
		MessageVersion.soap12 = new MessageVersion(EnvelopeVersion.Soap12, AddressingVersion.None);
		MessageVersion.soap11Addressing10 = new MessageVersion(EnvelopeVersion.Soap11, AddressingVersion.WSAddressing10);
		MessageVersion.soap12Addressing10 = new MessageVersion(EnvelopeVersion.Soap12, AddressingVersion.WSAddressing10);
		MessageVersion.soap11Addressing200408 = new MessageVersion(EnvelopeVersion.Soap11, AddressingVersion.WSAddressingAugust2004);
		MessageVersion.soap12Addressing200408 = new MessageVersion(EnvelopeVersion.Soap12, AddressingVersion.WSAddressingAugust2004);
	}

	private MessageVersion(EnvelopeVersion envelopeVersion, AddressingVersion addressingVersion)
	{
		this.envelope = envelopeVersion;
		this.addressing = addressingVersion;
	}

	[__DynamicallyInvokable]
	public static MessageVersion CreateVersion(EnvelopeVersion envelopeVersion)
	{
		return MessageVersion.CreateVersion(envelopeVersion, AddressingVersion.WSAddressing10);
	}

	[__DynamicallyInvokable]
	public static MessageVersion CreateVersion(EnvelopeVersion envelopeVersion, AddressingVersion addressingVersion)
	{
		if (envelopeVersion == null)
		{
			throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("envelopeVersion");
		}
		if (addressingVersion == null)
		{
			throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("addressingVersion");
		}
		if (envelopeVersion == EnvelopeVersion.Soap12)
		{
			if (addressingVersion == AddressingVersion.WSAddressing10)
			{
				return MessageVersion.soap12Addressing10;
			}
			if (addressingVersion == AddressingVersion.WSAddressingAugust2004)
			{
				return MessageVersion.soap12Addressing200408;
			}
			if (addressingVersion == AddressingVersion.None)
			{
				return MessageVersion.soap12;
			}
			throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("addressingVersion", SR.GetString("AddressingVersionNotSupported", addressingVersion));
		}
		if (envelopeVersion == EnvelopeVersion.Soap11)
		{
			if (addressingVersion == AddressingVersion.WSAddressing10)
			{
				return MessageVersion.soap11Addressing10;
			}
			if (addressingVersion == AddressingVersion.WSAddressingAugust2004)
			{
				return MessageVersion.soap11Addressing200408;
			}
			if (addressingVersion == AddressingVersion.None)
			{
				return MessageVersion.soap11;
			}
			throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("addressingVersion", SR.GetString("AddressingVersionNotSupported", addressingVersion));
		}
		if (envelopeVersion == EnvelopeVersion.None)
		{
			if (addressingVersion == AddressingVersion.None)
			{
				return MessageVersion.none;
			}
			throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("addressingVersion", SR.GetString("AddressingVersionNotSupported", addressingVersion));
		}
		throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("envelopeVersion", SR.GetString("EnvelopeVersionNotSupported", envelopeVersion));
	}

	[__DynamicallyInvokable]
	public override bool Equals(object obj)
	{
		return this == obj;
	}

	[__DynamicallyInvokable]
	public override int GetHashCode()
	{
		int num = 0;
		if (this.Envelope == EnvelopeVersion.Soap11)
		{
			num++;
		}
		if (this.Addressing == AddressingVersion.WSAddressingAugust2004)
		{
			num += 2;
		}
		return num;
	}

	[__DynamicallyInvokable]
	public override string ToString()
	{
		return SR.GetString("MessageVersionToStringFormat", this.envelope.ToString(), this.addressing.ToString());
	}

	internal bool IsMatch(MessageVersion messageVersion)
	{
		if (messageVersion == null)
		{
			throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("messageVersion");
		}
		if (this.addressing == null)
		{
			throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "MessageVersion.Addressing cannot be null")));
		}
		if (this.envelope != messageVersion.Envelope)
		{
			return false;
		}
		if (this.addressing.Namespace != messageVersion.Addressing.Namespace)
		{
			return false;
		}
		return true;
	}
}

        对象序列化图

            CreateMessage方法是用来设计序列化对象到Message消息体中的。为了达到这个目的,这些方法要接收object类型的参数。其中一种方法是使用WCF默认的序列化器,另一种方法是接受自定义序列化器。此外,除了这些参数外,还有接受String类型的参数。这个参数,在相关的Message对象的头块中设置WS-Affressing Action的值。

        从Reader提取数据

            CreateMessage方法接受XmlReader或XmlDictionaryReader作为参数。这些方法会拉出XmlDictionaryReader的整个内容到返回的Message对象里,或者到Message的body(消息体)里。CreateMessage方法被重载为多种方法,它们包含了读取整个消息和只读取消息体的参数。

        const int MAXHEADERSIZE = 500;
        //读取信封的例子
        //从包含消息的文件里读取数据
        FileStream stream = File.Open("entireMessage.xml", FileMode.Open);
        XmlDictionaryReader envelopeReader = XmlDictionaryReader.CreateTextReader(stream,new XmlDictionaryReaderQuotas());
        Message msg = Message.CreateMessage(envelopeReader, MAXHEADERSIZE, MessageVersion.Soap11WSAddressing10);
        Console.WriteLine("{0}\n",msg.ToString());
        //读取消息体的例子
        //从文件里只读消息体数据
        stream = File.Open("bodyContent.xml", FileMode.Open);
        XmlDictionaryReader bodyReader = XmlDictionaryReader.CreateTextReader(stream, new XmlDictionaryReaderQuotas());
        msg = Message.CreateMessage(MessageVersion.Soap12WSAddressing10, "urn:SomeAction", bodyReader);
        Console.WriteLine("{0}\n",msg.ToString());

        使用BodyWriter把数据放进Message

            CreateMessage其中一种重载方法允许调用者使用System.ServieModel.Channels.BodyWrter把数据推送到Message里。BodyWriter是一个抽象类型,它展示了接受XmlDictionaryWrite作为参数的OnWriteBodyContents包含方法。正是通过这种方法,BodyWrite的子类型可以对Message的创建过程产生影响,因此BodyWrite对影响消息的反序列化很有用。以下演示了BodyWriter子类型是如何从XML文件里读取数据并放入Message的消息体里的:

    class MyBodyWriter:BodyWriter
    {
        private String m_fileName;

        internal MyBodyWriter(string fileName) : base(true)
        {
            this.m_fileName = fileName;
        }

        protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
        {
            using (FileStream stream = File.Open(m_fileName, FileMode.Open))
            {
                XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(stream, new XmlDictionaryReaderQuotas());
                reader.ReadStartElement();
                while(reader.NodeType!= XmlNodeType.EndElement)
                {
                    writer.WriteNode(reader,true);
                }
            }
        }
    }

        Message和SOAP Fault

            Message类型定义了用来创建表示SOAP Fault的消息对象的工厂方法。SOAP Fault是SOAP消息的一种形式,用来表示错误信息。

Buffered vs Streamed消息

    当考虑消息在端点之间流动时,就会本能地想到缓存。换句话说,假设当程序接收到一个Message时,它已经知道了整个Message,这种方式称为缓存模式(buffering)。与之相对的就是流处理模式,它有两种流处理模式。第一种为推模型,指发送者按照自己的节奏推送字节流到接收者。当发送流数据时,发送者会把数据写到本地缓冲区直到写满为止,并且数据会发送给接收者,当数据到达时,接收者会从本地缓冲区读取数据。第二种称为拉模型。当发送流数据时,接收者会重复执行直到请求的数据发送完毕。WCF基础结构实现了第二种流处理方法。

序列化消息

    Message类型上的所有序列化方法名称都以Write开始的,而且这些方法都接受XmlWriter或XmlDictionaryWrite让类型的参数。消息的的实际序列化工作由XmlWriter或XmlDictionaryWriter对象完成,而不是直接由Message对象完成。Message类型同样定义了对于消息序列化进行粒度控制的方法,例如,WriteBody方法序列化body标签和元素到XmlWriter或XmlDictionaryWriter包装的Stream里。换句话说,WriteBodyContents方法序列化Body元素(没有Body标签)到XmlDictionaryWriter包装的Stream里。可序列化方法原型如下:

    void WriteStartEnvelope(XmlDictionaryWriter writer);
    void WriteStartBody(XmlDictionaryWriter writer);
    void WriteStartBody(XmlWriter writer);
    void WriteBody(XmlDictionaryWriter writer);
    void WriteBody(XmlWriter writer);
    void WriteBodyContents(XmlWriter writer);
    void WriteMessage(XmlDictionaryWriter writer);
    void WriteMessage(XmlWriter writer);

反序列化消息

    在接收程序里普遍存在的任务就是消息的反序列化。消息的反序列化是从一个序列化的消息中创建一个新的消息别称。泛型方法GetBody允许调用者反序列化消息体的内容到T类型的对象里。另一种方法接受一个XmlObjectSerializer参数,因此为消息体的反序列化提供扩展点。可序列化方法原型如下:

    public T GetBody();
    public T GetBody(XmlObjectSerializer serializer);

Message状态

    Message类型是多状态的,Message状态可以通过多种方式描述。和任何一个引用类型一样,Message状态是它的各个字段的组合。Message的属性State表示MessageState的一个私有字段值。MessageState是一个枚举类型,它定义了5个值:Created、Read、Written、Copied和Cloased。Message对象的数State的值会随着该对象方法的调用而改变。从内部来看,Message通过State属性管理Message对象上方法调用的顺序。

使用消息头

    消息头块被SOAP消息基础结构用来表示地址、路由和安全信息。WCF也完全支持SOAP的消息处理结构,它包含一些创建、序列化和分析SOAP消息头块的工具。Message类型是一个SOAP消息的CLR抽象,它定义的成员允许WCF基础结构使用发送或接收到的消息头块。Message类型的Headers属性提供了这项功能。MessageHeader是对SOAP消息头块的泛型CLR抽象,从广义上说,MessageHeaders是一组MessageHeader对象;EndpointAddress是对WS-Addressing endpoint规范的CLR抽象。

复制消息

    有时需要从现有的消息实例创建一个缓存模式的消息拷贝。创建Message的拷贝还是相当简单的,但是会带来消息内部状态的改变。如果使用不当,状态改变会给要复制的消息对象带来一些问题。当调用CreateBufferedCopy方法时,新消息的state属性必须为MessageState.Created。如果设置其他状态,该方法就会抛出一个IncalidOperationExecption异常。直到CreateBufferedCopy返回结果,原调用实例的状态才会变为MessageState.Copied。如果该方法调用成功,则会返回一个System.ServiceModel.channels.MessageBuffer类型的实例。这个实例的状态会是Message.Created。以下代码演示了如何复制一个消息:

        Message msg = Message.CreateMessage(MessageVersion.Default, "urn:SomeAction", "Something in the body");
        MessageBuffer buffer = msg.CreateBufferedCopy(int.MaxValue);
        Message msgNew = buffer.CreateMessage();

清理消息

    Message类型实现了IDisposable接口,并且定义了一个Close方法。

                


    

你可能感兴趣的:(读书笔记)