第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