在这篇文章中,将会包括:
在契约优先(contract-first)开发方式中,一个最重要的步骤之一就是从XML结构生成在服务中使用的数据类型,这表现为契约。作为一个统一的分布式通信开发平台,在WCF开发中,支持这种数据契约(DataContract)生成是相当普遍的。
如果你还不熟悉契约优先(contract-first)的开发方法,可以从Aaron Skonnard的MSDN文章Contract-First Service Development获得快速概览,网址是:http://msdn.microsoft.com/en-us/magazine/cc163800.aspx
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 { } }
[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):
我们在这里生成的数据契约(DataContract)包含两种典型的类类型——一个复合类型和一个简单枚举类型。在大多数情况下,会在服务里定义更多复杂数据类型,并且WCF DataContractSerializer能够为基于XML结构的契约和基于代码的.NET类型之间的映射提供足够的支持。可在MSDN数据契约结构参考文档获取关于类型映射的更多信息:http://msdn.microsoft.com/en-us/library/ms733112.aspx
默认情况下,WCF运行时使用DataContractSerializer来完成数据序列化和反序列化。然而,在一些情况下,我们会倾向于使用XMLSerializer,这样开发者可以在序列化XML内容时获得更多控制权或者可以与POX客户端更紧密的配合(类似ASMX Web服务客户端)。
[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; } }
[ServiceContract] [XmlSerializerFormat(Style = OperationFormatStyle.Document)] public interface ITestService { [OperationContract] void SendUser(User user); }
当我们在ServiceContract应用XmlSerializerFormatAttribute,WCF运行时将使用XMLSerializer作为默认序列化器来序列化数据和反序列化SOAP消息。另外,自动生成的服务元数据将输出基于类的XML序列化特性指定的数据类型元数据。对于在前面代码示例中提及的用户类型,服务元数据将使用下面的截图所显示的结构来表示它的XML格式:
通过捕获底层的SOAP消息,我们会发现序列化的用户对象的XML内容符合之前定义的元数据结构,这是应用在用户类型上的那些XML序列化特性控制的(见下面的截图):
DataContract能够帮助我们设计在一个WCF服务中使用的数据类型。然而,这仅仅覆盖了底层SOAP消息中数据成员(在操作中使用的变量和参数)的序列化。有时我们也需要控制整个SOAP消息的结构和格式。
WCF采用了MessageContract的概念,能帮助服务开发者模拟一个已给定服务操作的完整消息的结构和格式。事实上,我们可以拿MessageContract类型作为一种特殊的DataContract类型,通过MessageContractAttribute来标记。这一部分将向你展示如何为我们的WCF服务操作定义一个典型的MessageContract,来控制底层SOAP XML消息的格式。
[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; } }
[OperationContract] HelloResponse SayHello(HelloRequest req);
使用MessageContractAttribute标记的类型能被用来表示完整的SOAP封装体。这种类型序列化仍然遵循普遍的DataContract类型规则。另外,使用MessageContract来控制SOAP封装,操作只能有单一的输入参数和返回值,明白这一点是很重要的。这是因为仅有的输入参数将被序列化为整个SOAP请求体,而返回值将被序列化为整个SOAP的相应体。
通过在通信网络上捕获SOAP请求/相应,能够发现序列化的SOAP消息内容符合MessageContract定义(见以下两个截图):