第9章 契约
9.1 契约的定义
WCF契约是标注了特定属性的.net类型,而且这些被标注的类型定义可以用来产生符合行业标准的WSDL和XSD文档。WCF契约会把这些类型映射为服务、操作、消息和消息中的部分。WCF有5种类型的契约:服务契约、操作契约、数据契约、消息契约和错误契约。
9.2 WCF契约剖析
服务契约
using System; using System.ServiceModel; namespace ConsoleApplication20 { [ServiceContract] interface IAndersService { [OperationContract] Int32? RequestReservation(DateTime? resDateTime); [OperationContract(IsOneWay = true)] void CancelReservation(Int32? reservationId); } }
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, Inherited = false, AllowMultiple = false)] public sealed class ServiceContractAttribute : Attribute { [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] public ServiceContractAttribute(); public Type CallbackContract { get; set; } //定义双工契约时使用 public string ConfigurationName { get; set; } //配置文件中引用服务的简称 public bool HasProtectionLevel { get; } public string Name { get; set; } //服务的名称 public string Namespace { get; set; } //服务的命名空间 public ProtectionLevel ProtectionLevel { get; set; } //当使用一个服务契约时,绑定必须达到的信息安全级别 public SessionMode SessionMode { get; set; } //表示程序里使用的通道有必要,能够或不需要使用会话通道形状。它的枚举值是Allowed,Required和NotAllowed }
操作契约
[AttributeUsage(AttributeTargets.Method)] public sealed class OperationContractAttribute : Attribute { public OperationContractAttribute(); public string Action { get; set; } public bool AsyncPattern { get; set; } //表示操作时否是异步编程模型的一部分 public bool HasProtectionLevel { get; } public bool IsInitiating { get; set; } public bool IsOneWay { get; set; } //默认的情况下,所有的操作都是用请求/应答消息交换模式。如果想要使用数据表消息交换模式,则在这个方法上再设置IsOneWay属性为true public bool IsTerminating { get; set; } public string Name { get; set; } public ProtectionLevel ProtectionLevel { get; set; } public string ReplyAction { get; set; } //该属性可以把WS-Addressing的action与应答消息关联起来 }
数据契约
数据契约会把.net类型映射到消息体上,而且它是消息序列化和反序列化的关键部分。数据契约中重要的属性就是DataContractAttribute和DataMemberAttribute。
using System.Runtime.Serialization; namespace ConsoleApplication20 { [DataContract] class Person { [DataMember(Name="Age")] int age; [DataMember(Name="Name")] string name; public int Age { get { return age; } set { age = value; } } public string Name { get { return name; } set { name = value; } } } }
DataContractAttribute可以应用到枚举、结构体和类上。
DataMemberAttribute类型可以应用到类成员和属性上。
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, Inherited = false, AllowMultiple = false)] public sealed class DataMemberAttribute : Attribute { public DataMemberAttribute(); public bool EmitDefaultValue { get; set; } //表示是否需要从序列化的数据里提取默认值。对于引用类型,默认值是null,值类型是0. public bool IsRequired { get; set; } //表示成员是否需要出现在序列化的数据里。 public string Name { get; set; } //把类型成员的名字映射到序列化数据的元素上 public int Order { get; set; } //表示元素在序列化数据里的顺序 }
消息契约
所有的消息契约必须实现一个公开的、无参的构造函数
using System; using System.ServiceModel; namespace ConsoleApplication20 { [MessageContract(WrapperName="MyMessageContract", WrapperNamespace="http://andersoft.com/anders")] class MyMessageContract { [MessageHeader(Name = "reservationId", MustUnderstand = true)] private Int32? _reservationId; [MessageBodyMember(Name = "newDateTime")] private DateTime? _newDateTime; public Int32? ReservationId { get { return _reservationId; } set { _reservationId = value; } } public DateTime? NewDateTime { get { return _newDateTime; } set { _newDateTime = value; } } public MyMessageContract() { } } }
操作兼容性
WCF把这些消息分为Typed和UnTyped两大类。Typed消息包括消息契约和System.ServiceModel.Channels.Message类型,而UnTyped消息包括数据契约和其他可序列化的类型。
using System; using System.Runtime.Serialization; using System.ServiceModel; using System.ServiceModel.Channels; namespace ConsoleApplication20 { [ServiceContract] interface IAndersService { //UnTyped Message [OperationContract] Int32? RequestReservation1(DateTime? resDateTime); [OperationContract(IsOneWay = true)] void CancelReservation1(SomeDataContract dataContract); //Typed Message [OperationContract] Int32? RequestReservation2(Message message); [OperationContract] void CancelReservation2(MyMessageContract messageContract); } [DataContract] class SomeDataContract { } }
我对契约的看法
1. 避免在数据契约和消息契约里定义方法
2. 密封契约
3. 使用可空类型
9.3 从契约定义到契约对象
WCF基础结构定义了几个可以使用反射读取契约元数据的类型。
System.ServiceModel.Description.ContractDescription
System.ServiceModel.Description.OperationDescription
System.ServiceModel.Description.MessageDescription
ContractDescription类型描述了服务中的所有操作。
ContractDescription类型封装了一个OperationDescription和一个MessageDescription集合。
ContractDescription类型定义个一个GetContract的工厂方法,它可以接受一个类型参数。这个类型参数会作为数据契约使用。
ContractDescription andersServiceDescription = ContractDescription.GetContract(typeof(IAndersService)); foreach (OperationDescription operationDes in andersServiceDescription.Operations) Console.WriteLine(operationDes.Name); //RequestReservation1 //CancelReservation1 //RequestReservation2 //CancelReservation2