WCF服务契约 听课笔记
服务契约
服务契约定义了向外部暴露的行为,它既可以应用于接口也可以应用于具体的类。但是考虑到某些类可能会实现多个接口如果定义在Class上,就只能实现一种契约,此外Class是易变的,而Interface是相对稳定的,所以最好把服务契约定义在Interface上。如果标记了ServiceContractAttribute那么在内部域中必须包含或者说是至少包含一个操作契约(OperateContractAttribute)标记。
q 在服务契约中,通过指定Name属性可以改变在客户端使用的类型名称。
q 在方法签名中使用MesageParameterAttribute,例如:
void SaveLinkItem([MessageParameter(Name = “EasyFlowLinkItem”)] LinkItem item) |
使用这种标记后,在客户端通过代理调用时方法的参数将是EasyFlowLinkItem而不是item. 尤其在多个系统交互时,可能方法的参数名称不同,通过这个属性完成SOAP消息中方法参数的映射,在从客户端发送到服务端后,WCF将完成这个名称的映射。
q 标记为服务契约的接口或类是不具备一些OO特性的,例如继承性和多态。
数据契约
q 数据契约在功能上可以认为是同于SerialzableAttribute和IxmlSerializable的,都表示支持XML序列化,以便能够封送SOAP消息(XML+XSD)在客户端和服务器端往返。
q 当类被标记为可XML序列化后,在整个域内部不论可访问性如何都被认为是可被序列化的。
q 数据契约在定义中应该按照W3C的规范在Namespace属性值中加上schema前缀。
q 两个常用属性 IsRequired 表示在序列化之前必须被赋值,Order属性表示按照制定的顺序生成节点。如果没有指定,则按照字符表顺序生成。
q 数据契约标记应该在属性上声明,而不应该在内部字段上使用。否则就不符合OO的设计原则。
q 应用KnownType属性。
[ServiceContract] public interface LinkTestService { [OperationContract] void SomeMehtod(LinkItem item); } [DataContract] [KnownType(typeof(LinkItemA))] public class LinkItem { [DataMember] public string Id; } [DataContract] public class LinkItemA : LinkItem { [DataMember] public string Location; } |
在LinkItem中如果没有标记KnownTypeAttribute,那么在客户端调用LinkTestService.SomeMethod方法的时候,如果传入了LinkItemA则WCF是服务识别的。注意派生类中也应该标记DataMember。定义标记的方式可以有两种,一种是通过如上图所示的代码方式,另外一种是在Host中使用配置文件。
除此之外,还可以使用ServiceKownTypeAttribute标记的服务契约上,指明在序列化和反序列化时可以识别的类型。还可以把ServiceKnownType标记在方法上,进一步限定只有标记这这些属性的方法可以使用派生类型。
消息契约
q 提供对自定义SOAP message的支持
q 允许添加自定义Header
q 控制消息是否被包装
q 控制签名和加密
q MessageHeaderAttribute和MessageBodyAttribute 提供对消息头和消息体的控制。他们可以确定那些程序可以被包装在消息头或消息体中。
自定义
在实际的开发中常常面临多个产品或系统集成的问题,由于早期并没有考虑到集成所以在设计上可能会遇到一些无法直接序列化的对象。对于这些对象需要自定义的实现IXmlSerializable方法并标记XmlSchemaProviderAttribute。
通过IXmlSerializable的ReadXml和WriteXml为WSDL和元数据交换ImetaDataExchange提供可识别的XSD。假设LinkItem不支持直接序列化,那么通过在服务端定义LinkItemSerializer新的对象,实现自定义序列化工作。
[XmlSchemaProvider("GetSchema")] public class LinkItemSerializer:IXmlSerializable {
public string Id; #region IXmlSerializable 成员 public System.Xml.Schema.XmlSchema GetSchema() { //TODO } public void ReadXml(System.Xml.XmlReader reader) { //TODO } public void WriteXml(System.Xml.XmlWriter writer) { //TODO } #endregion } |
这些更改不会影响到客户端的调用方式,客户端仍然使用原来的LinkItem进行传递。