WCF 4 Cookbook 系列(一) 使用契约 [中]

在这篇文章中,将会包括:

  • 定义一个one-way契约
  • 使DataContract向前兼容
  • 从XML Schema生成DataContract
  • 使用XMLSerializer控制消息序列化方式
  • 使用MessageContract控制SOAP消息
  • 通过Contract添加一个自定义SoapHeader
  • 通过FaultContract返回自定义异常数据

从XML Schema生成DataContract

在契约优先(contract-first)开发方式中,一个最重要的步骤之一就是从XML结构生成在服务中使用的数据类型,这表现为契约。作为一个统一的分布式通信开发平台,在WCF开发中,支持这种数据契约(DataContract)生成是相当普遍的。

准备工作

如果你还不熟悉契约优先(contract-first)的开发方法,可以从Aaron Skonnard的MSDN文章Contract-First Service Development获得快速概览,网址是:http://msdn.microsoft.com/en-us/magazine/cc163800.aspx

怎么做…

  1. 编写在我们的WCF服务中使用的用来表示DataContract类型的XML结构。下面的截图显示了一个简单的示例结构,它包含一个简单的枚举和一个复杂数据类型定义:Packtpub.Microsoft.WCF.4.0.Cookbook.for.Developing.SOA.Applications.Oct
  2. 使用WCF服务模型元数据通用工具(Svcutil.exe)生成在第一步中基于XML结构的DataContract类型源码。下面是使用Svcutil.exe工具的示例命令:
    svcutil.exe /target:code /dataContractOnly /serializer:DataContractSerializer /importXmlTypes TestDataContractSchema.xsd
    生成的DataContract如下:
    public enum LevelEnum : int
    {
        [System.Runtime.Serialization.EnumMemberAttribute()]
        Low = 2,
        ……………
    }
    
    [System.Runtime.Serialization.DataContractAttribute(Name=
    "TestData", Namespace="http://wcftest.org/datacontract")]
    public partial class TestData : object,
    System.Runtime.Serialization.IExtensibleDataObject
    {
    …………………
        [System.Runtime.Serialization.DataMemberAttribute
        (IsRequired=true, Order=2)]
        public wcftest.org.datacontract.LevelEnum EnumProperty
        { }
    }
  3. 使用在我们的WCF服务生成的DataContract作为操作的参数或者返回值。
    [ServiceContract]
    public interface ITestService
    {
        [OperationContract]
        TestData GetData();
    }

原理…

契约优先开发方法是契约/结构(contract/schema)驱动的;开发者需要编写服务/数据(service/data)的元数据/契约(metadata/contract)。对于前面的示例,TestDataContractSchema.xsd提供了将在WCF服务中使用的两种类型的契约定义。

Svcutil.exe是.NET 3.5 SDK中提供的非常有用的工具。如果你熟悉ASP.NET ASMX Web服务开发,你会发现它和wsdl.exe工具非常类似。你可以生成WCF客户端代理和从服务代码导出元数据。在这里我们只是使用它从已给出的XML结构生成了序列化代码。在前面的示例中,我们指定DataContractSerializer来序列化类型(如果你喜欢面向XML序列化代码,你也可以使用XMLSerializer)。

通过在通信网络上捕获服务操作的底层SOAP消息(参见下面的截图),我们可以发现返回值的XML信息和我们作为生成源提供的XML结构一致(TestDataContractSchema.xsd):
Packtpub.Microsoft.WCF.4.0.Cookbook.for.Developing.SOA.Applications.Oct

更多…

我们在这里生成的数据契约(DataContract)包含两种典型的类类型——一个复合类型和一个简单枚举类型。在大多数情况下,会在服务里定义更多复杂数据类型,并且WCF DataContractSerializer能够为基于XML结构的契约和基于代码的.NET类型之间的映射提供足够的支持。可在MSDN数据契约结构参考文档获取关于类型映射的更多信息:http://msdn.microsoft.com/en-us/library/ms733112.aspx

使用XMLSerializer控制消息序列化方式

默认情况下,WCF运行时使用DataContractSerializer来完成数据序列化和反序列化。然而,在一些情况下,我们会倾向于使用XMLSerializer,这样开发者可以在序列化XML内容时获得更多控制权或者可以与POX客户端更紧密的配合(类似ASMX Web服务客户端)。

怎么做…

  1. 首先,我们应该为XMLSerializer准备数据类型——通过数据类型上添加XML序列化特性来实现。接下来,使用数个XML序列化特性来声明用户类型(XmlRootAttribute用来声明顶级类型,XmlElementAttribute用来声明类型成员)。
    [XmlRoot(ElementName = "UserObject", Namespace = "http://wcftest.org/xmlserializer")]
    public class User
    {
        [XmlElement(ElementName = "FName")]
        public string FirstName { get; set; }
    
        [XmlElement(ElementName = "LName")]
        public string LastName { get; set; }
    
        [XmlElement(ElementName = "IsEnabled")]
        public bool Enabled { get; set; }
    }
  2. 然后,我们需要在ServiceContract类型上应用XmlSerializerFormatAttribute特性(见代码片段中的ITestService接口)。
    [ServiceContract]
    [XmlSerializerFormat(Style = OperationFormatStyle.Document)]
    public interface ITestService
    {
        [OperationContract]
        void SendUser(User user);
    }

原理…

当我们在ServiceContract应用XmlSerializerFormatAttribute,WCF运行时将使用XMLSerializer作为默认序列化器来序列化数据和反序列化SOAP消息。另外,自动生成的服务元数据将输出基于类的XML序列化特性指定的数据类型元数据。对于在前面代码示例中提及的用户类型,服务元数据将使用下面的截图所显示的结构来表示它的XML格式:

Packtpub.Microsoft.WCF.4.0.Cookbook.for.Developing.SOA.Applications.Oct

通过捕获底层的SOAP消息,我们会发现序列化的用户对象的XML内容符合之前定义的元数据结构,这是应用在用户类型上的那些XML序列化特性控制的(见下面的截图):

Packtpub.Microsoft.WCF.4.0.Cookbook.for.Developing.SOA.Applications.Oct

使用MessageContract控制SOAP消息

DataContract能够帮助我们设计在一个WCF服务中使用的数据类型。然而,这仅仅覆盖了底层SOAP消息中数据成员(在操作中使用的变量和参数)的序列化。有时我们也需要控制整个SOAP消息的结构和格式。

WCF采用了MessageContract的概念,能帮助服务开发者模拟一个已给定服务操作的完整消息的结构和格式。事实上,我们可以拿MessageContract类型作为一种特殊的DataContract类型,通过MessageContractAttribute来标记。这一部分将向你展示如何为我们的WCF服务操作定义一个典型的MessageContract,来控制底层SOAP XML消息的格式。

怎么做…

  1. 定义一个用户数据类型表示完整的SOAP消息体内容。使用MessageContractAttribute修饰这个类型,使用MessageBodyMemberAttribute标记类成员,这样就能被嵌入到SOAP消息体中。下面的代码展示了一个MessageContract示例的部分——一个演示的是操作请求,另一个是操作响应。
    [MessageContract(WrapperName = "Hello", WrapperNamespace =
    "http:// wcftest.org/messagecontract")]
    public class HelloRequest
    {
        [MessageBodyMember(Name = "Who")]
        public string User { get; set; }
    }
    
    [MessageContract(WrapperName = "HelloResponse", WrapperNamespace =
    "http://wcftest.org/messagecontract")]
    public class HelloResponse
    {
        [MessageBodyMember(Name = "Reply")]
        public string ReplyContent { get; set; }
    }
  2. 为请求/响应操作定义了MessageContract类型之后,我们需要使用它们作为输入参数(仅仅作为输入参数)和操作执行的返回值(见下面的SayHello操作)。
    [OperationContract] 
    HelloResponse SayHello(HelloRequest req);
  3. 在SayHello操作中, HelloRequest仅仅是输入参数,HelloResponse表示返回值。

原理…

使用MessageContractAttribute标记的类型能被用来表示完整的SOAP封装体。这种类型序列化仍然遵循普遍的DataContract类型规则。另外,使用MessageContract来控制SOAP封装,操作只能有单一的输入参数和返回值,明白这一点是很重要的。这是因为仅有的输入参数将被序列化为整个SOAP请求体,而返回值将被序列化为整个SOAP的相应体。

通过在通信网络上捕获SOAP请求/相应,能够发现序列化的SOAP消息内容符合MessageContract定义(见以下两个截图):

MessageContract1

MessageContract2

posted @ 2012-02-06 21:29  brycezhang  阅读(687)  评论(0编辑  收藏  举报