WCF 消息传递基础

WCF 消息传递基础目录

  XML 表示形式

  Message 类

  消息版本

  读写消息

  键入的消息正文

  消息生存期

  消息标头和属性

  将消息映射到方法

  端点和绑定

  综述

  开始分离 Windows® Communication Foundation 的各层时,您会发现一种复杂的基于 XML 的消息传递框架,它在使用各种协议和格式连接系统时提供了大量的可能性。在本月的专栏中,我将着重介绍一些主要的消息传递功能,它们提供了这样的灵活性。

  本专栏假定您对 Windows Communication Foundation 编程模型具有基本的了解。如果您不熟悉它,那么在继续之前,您应该阅读 2006 年 2 月份的《MSDN® 杂志》中我的文章。

  Windows Communication Foundation 消息传递体系结构的主要目的之一是,在提供统一编程模型的同时,还允许灵活地表示数据和传递消息。这是基于将 XML 作为数据模型以及将 SOAP 和 WS-Addressing 作为消息传递框架而实现的。但是,Windows Communication Foundation 构建在这些模型基础上这一事实,并不意味着在传递消息时必须使用 XML 1.0、SOAP 或 WS-Addressing。您将会看到 Windows Communication Foundation 提供了很大的灵活性。

  XML 表示形式

  从早期的 XML 开始,软件行业就依赖于为在 XML 文档中找到的数据提供标准定义的鲜为人知的规范。此规范称为 XML 信息集 (InfoSet),它根据元素和属性所包含的信息来定义它们,在某种意义上这完全与字节表示形式无关。(有关详细信息,请参阅 www.w3.org/TR/xml-infoset。)

  InfoSet 规范使得其他 XML 规范和 API 为在 XML 文档中找到的数据提供一致的视图成为可能(尽管它们可能以完全不同的方式表示该数据)。最终,InfoSet 为使用 XML 数据的应用程序提供了共同的集合点,如图 1 所示。最后要说的是,XML 处理器负责在字节表示形式和编程模型体验之间的转换。

WCF 消息传递基础

  图 1XML InfoSet 的角色

  Windows Communication Foundation 为 System.Xml 命名空间引入了一些基本增强,这使得在读写 XML 文档时利用替代的字节表示形式(而不是仅限文本的 XML 1.0)成为可能。此处关注的主要类 System.Xml.XmlDictionaryReader 和 System.Xml.XmlDictionaryWriter 位于 Microsoft® .NET Framework 3.0 附带的新 System.Runtime.Serialization 程序集中。

  XmlDictionaryReader 和 XmlDictionaryWriter 类都提供了静态工厂方法,用于创建使用文本、二进制和 MTOM(消息传输优化机制)表示形式的读取器和编写器。例如,XmlDictionaryReader 提供了 CreateTextReader、CreateBinaryReader 和 CreateMtomReader 方法,而 XmlDictionaryWriter 提供了对应的 CreateTextWriter、CreateBinaryWriter 和 CreateMtomWriter 方法。

  让我们看一些示例。试考虑某个客户的以下文本 XML 1.0 表示形式:

<!-- customer.xml -->
<Customer xmlns="http://example.org/customer">
  <Email>bob@contoso.com</Email>
  <Name>Bob</Name>
</Customer>

  以下代码说明如何将 customer.xml 读入 XmlDocument 对象,以及如何使用 XmlDictionaryWriter 以二进制表示形式重新将同一 XmlDocument 对象保存在磁盘上:

// read from XML 1.0 text representation
XmlDocument doc = new XmlDocument();
doc.Load("customer.xml");
// write to binary representation
FileStream custBinStream = new FileStream(
  "customer.bin", FileMode.Create);
using (XmlWriter xw = XmlDictionaryWriter.CreateBinaryWriter(
  custBinStream))
{
  doc.WriteContentTo(xw);
}

   图 4客户 XML 文档的 MTOM 表示形式

  现在,通过将 MTOM 表示形式再读入 XmlDocument 并以文本表示形式将其保存在磁盘上,我们可以执行一个完整循环,如下所示:

// read from MTOM representation
XmlDocument doc = new XmlDocument();
FileStream custMtomStream = new FileStream(
  "customer.mtom", FileMode.Open);
using (XmlReader xr = XmlDictionaryReader.CreateMtomReader(
  custMtomStream, Encoding.UTF8, XmlDictionaryReaderQuotas.Max))
{
  doc.Load(xr);
}
// write to text (XML 1.0) representation
doc.Save("customer.xml");

  生成的 custom.xml 文件应该与原始的文本表示形式完全相同。正如您所看到的,通过提供三种方法来表示 XML 以实现持久性和传输,这些 System.Xml 增强功能提供了很大的灵活性。当然,将来可能会添加其他表示形式。此外,虽然是 Windows Communication Foundation 为表带来了这些增强,但是可以在任何 .NET Framework 3.0 应用程序中利用它们。

  您可以选择在传递消息时使用的 XML 表示形式,Windows Communication Foundation 即基于此而构建。如果需要互操作性,则应该选择 XML 1.0 文本表示形式。如果需要互操作性以及对二进制负载的高效支持,则应该选择 MTOM 表示形式。在只有 .NET 的方案中,二进制表示形式可能会提供更佳的性能。此处的关键是您确实可以进行选择了。

  Message 类

  任何消息传递框架的另一个主要功能是,通过任意标头扩展消息负载。标头不过是随消息传递的额外信息,用于实现其他的消息处理功能(如安全性、可靠的消息传递和事务)。对于 XML 消息,这意味着用 XML 标头扩展 XML 负载(两者都表示为在容器元素中分帧的 XML 元素)。此功能与 SOAP 提供的完全相同。

通过调用各种静态 CreateMessage 重载之一创建 Message 对象,并使用 IDisposable 或通过显式调用 Close 处理 Message 对象。可以从头开始创建新的 Message 对象,在发送消息时通常这样做。也可以从消息流创建新的 Message 对象,在接收消息时通常这样做。

  如果从头开始创建消息,则必须指定操作、消息版本以及要在消息中使用的正文。操作唯一地标识消息的目的或语义。Windows Communication Foundation 服务依赖于将传入消息分派给相应方法的操作。消息版本标识传输时使用的 SOAP 和 WS-Addressing 版本(如果有)。指定消息版本时可以选择各种不同的选项。让我们了解一下这些选项。

  消息版本

  如上所述,通过 MessageVersion 类,可以指定要使用的 SOAP 和 WS-Addressing 版本。通过调用 CreateVersion 并提供 EnvelopeVersion 对象(用于标识 SOAP 版本)和 AddressingVersion 对象(用于标识 WS-Addressing 版本),可以创建 MessageVersion 对象。如下所示:

MessageVersion version = MessageVersion.CreateVersion(
  EnvelopeVersion.Soap12, AddressingVersion.WSAddressing10);

  如果查看一下 EnvelopeVersion 类,则将看到 Windows Communication Foundation 当前支持三个选项(即 SOAP-None、Soap11 和 Soap12),如下所示:

public sealed class EnvelopeVersion
{
  public static EnvelopeVersion None { get; }
  public static EnvelopeVersion Soap11 { get; }
  public static EnvelopeVersion Soap12 { get; }
  ...
}

  同样,如果查看一下 AddressingVersion,则将看到 Windows Communication Foundation 当前也支持三个选项(即 WS-Addressing-None、WSAddressing10 和 WSAddressingAugust2004),如下所示:

public sealed class AddressingVersion
{
  public static AddressingVersion None { get; }
  public static AddressingVersion WSAddressing10 { get; }
  public static AddressingVersion WSAddressingAugust2004 { get;}
  ...
}


读写消息

  从头开始创建消息时,可以使用许多重载来指定消息的正文。可以将正文作为 XmlDictionaryReader、XmlReader 或可序列化的对象提供。还可以为正文提供 MessageFault 对象以便创建故障消息。创建消息后,可以通过调用返回 XmlReader 的 GetReaderAtBodyContents 或调用 GetBody<T> 将正文反序列化为 .NET 对象来访问正文。

  在希望写入消息时,可以通过分别调用 WriteMessage 或 WriteBody 方法写入整个消息或仅写入正文。这两种方法都具有允许提供 XmlDictionaryWriter 或 XmlWriter 对象的重载。以下示例说明如何创建随后写入到名为 message.xml 的文件中的新消息:

// load body from customer.xml file
XmlDocument doc = new XmlDocument();
doc.Load("customer.xml");
Message m = Message.CreateMessage(
  MessageVersion.Soap11WSAddressingAugust2004,
  "urn:add-customer", new XmlNodeReader(doc));
FileStream fs = new FileStream("message.xml", FileMode.Create);
using (XmlWriter xw = XmlDictionaryWriter.CreateTextWriter(fs))
{
  m.WriteMessage(xw);
}

  在这种情况下,为操作指定了“urn:add-customer”,为消息版本指定了 Soap11WSAddressing2004。正文是通过 XmlReader 提供的。然后调用 WriteMessage 将消息写出到基于文本的 XmlDictionaryWriter 对象。生成的 message.xml 如下所示:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"
  xmlns:a="http://schemas.xmlsoap.org/ws/2004/08/addressing">
 <s:Header>
  <a:Action s:mustUnderstand="1">urn:add-customer</a:Action>
 </s:Header>
 <s:Body>
  <Customer xmlns="http://example.org/customer">
   <Email>bob@contoso.com</Email>
   <Name>Bob</Name>
  </Customer>
 </s:Body>
</s:Envelope>


正如您可以看到的,Message 类为使用不同版本的 SOAP 和 WS-Addressing(如果使用了它们中的任一个)读写消息提供了很大的灵活性。此外,它基于 XmlDictionaryReader 执行读写操作的这一事实意味着,在传递消息时可以使用所有支持的 XML 表示形式。

  键入的消息正文

  到目前为止,我介绍了直接使用 XML 的示例。如果要将 XML 反序列化为 .NET 对象,则可以使用支持的序列化程序之一,如 DataContractSerializer、NetDataContractSerializer 或 XmlSerializer。在与 DataContractSerializer 一起使用时,以下类能够表示在 customer.xml 中找到的 XML:

[DataContract(Namespace="http://example.org/customer")]
public class Customer
{
  public Customer() { }
  public Customer(string name, string email)
  {
    this.Name = name;
    this.Email = email;
  }
  [DataMember]
  public string Name;
  [DataMember]
  public string Email;
}

  现在,可以通过为正文提供 Customer 对象创建消息,如下所示:

Customer cust = new Customer("Bob", "bob@contoso.com");
Message msg = Message.CreateMessage(
  MessageVersion.Soap11WSAddressingAugust2004,
  "urn:add-customer", cust);

  尽管使用一个对象来表示正文,但是生成的消息将与前面的示例完全相同。

  现在,读取正文时,可以使用 GetBody<Customer> 生成新的 Customer 对象,如下所示:

Customer c = msg.GetBody<Customer>();
Console.WriteLine("Name: {0}, Email: {1}", c.Name, c.Email);




 

posted @ 2011-10-22 15:53  spirit1  阅读(178)  评论(0编辑  收藏  举报