1. WCF 基础之契约(Contract)
契约(Contract)是 WCF 的消息标准,告知客户端如何与服务器联系交互。契约是平台中立的,也就说我们可以使用其它平台(包括开发和系统平台)来调用服务。WCF 中包含 4 种契约,分别是用于定义服务操作(Operations)的 Service Contract,定义自定义数据结构的 Data Contract,定义错误异常的 *Fault Contract,以及直接控制消息格式的 Message Contract。它们算是WCF的核心之一,如果你要使用WCF,就需要了解他们。
2. 服务契约(Service Contract)
一般情况下,我们用接口(Interface)来定义服务契约(Service Contract)。虽然我们也可以使用 Class,但使用接口的好处更明显一些。便于契约的继承,不同根的类型可以自由实现相同的契约。
WCF 使用特性 ServiceContractAttribute 标定服务契约,OperationContractAttribute 标定服务方法。
ServiceContract public interface ICalculate { OperationContract double Add(double a, double b); }
OperationContract 只能用于 Method,只有添加了此特性的方法才能被客户端调用。它甚至可以用来标注私有方法,这显然超出了面向对象的规则,而更多的是 SOA 的方式。SOA 只是分布式系统的架构体系,在架构体系内部我们依然采取面向对象的原则来编码,所以标注私有方法是不被推荐的,这也是使用接口作为服务契约的一个好处(接口中无法定义私有方法)。
异步 Asynchronous
在WCF中,如果在服务契约中建立了异步方法,那么客户端代理将同时存在异步和非异步版本。
设置OperationContract的AsyncPattern:=True 即可以建立异步方法
双向通讯 Duplex
通过在服务契约中指定CallbackContract:=GetType(ICalculatorDuplexCallback)即可以指定回调接口。 在回调的时候,由于系统会确认执行过程。所以,为了避免死锁,一般加上(IsOneWay:=True)>关键字。
Oneway
在我们的程序中,有些服务是不需要确认是否到达到服务器的或者没有返回值的。这个时候我们可以设置IsOneWay:=True 这样,WCF便不会等到执行完才返回了。
Session
虽然在SOA中,提议每个服务请求都是独立的。但是,我们有时候需要保存一些数据,比如:用户ID。这个时候,我们可以把这个数据保存回话中。 WCF中,支持3种回话模式
1. Pre-Call: 服务实例被释放,客户端抛出 FaultException,客户端代理对象无法继续使用。
2. Pre-Session: 服务实例被释放,会话终止。客户端抛出 FaultException,客户端代理对象无法继续使用。
3. Singleton: 服务实例依旧运行,会话终止。客户端抛出 FaultException,客户端代理对象无法继续使用。
流 Stream
在WCF中也支持流传输,这时候,有点类似打开TCP端口~。对于传输大量数据比较有效率,例如:上传文件。记得在使用的时候配置好最大消息的大小
ServiceContract 的属性
3. 数据契约(Data Contract)
数据契约(Data Contract)是用来标识用户自定义类型和序列化。
DataContractAttribute、DataMemberAttribute 来标注自定义数据类型,这样我们就可以在服务方法中传递复杂的数据体了。使用之前,我们需要添加 System.Runtime.Serialization.dll 引用。由此我们可以看出其基本的开发模式,那就是使用 ServiceContract、OperationContract 执行运算,而使用 DataContract、DataMember 作为可序列化的数据载体。当然,我们也可以使用 "Serializable" 代替 "DataContract"。
其实,数据契约主要是定义数据的格式(契约)。DataMember()是告诉序列化引擎要序列化的那个部分(关于序列化,注意一点,反序列化时可以访问任一字段,作用域关键字不起作用,这可能会暴露安全问题。)
DataContract public struct Number
{
DataMember public double Num1;
DataMember public double Num2;
public Number(double num1, double num2)
{
this.Num1 = num1;
this.Num2 = num2;
}
}
DataContract 的属性
Name / Namespace:自定义名称和命名空间。
DataMember 的属性
Name:自定义名称。
IsRequired:指示该成员序列化前必须被赋值。
DataContractSerializer 实际上序列化是一个过程,不过这个过程大多徐情况下被系统自动实现了。默认情况下,WCF 使用 DataContractSerializer 引擎对相关参数进行序列化,这也是 WCF 推荐的方式。另外一个选择是 XmlSerializer,也就是 ASP.NET Web Service 所使用的序列化引擎。XmlSerializer 仅支持 DataContractSerializer 所支持的部分类型,但它允许你使用 XmlAttributeAttribute 等特性对序列化生成的 XML 进行更多的控制。
DataContractSerializer 支持的类型:
支持所有的基本类型,还包括 XmlElement 和 DateTime 这样的常用类型。
支持使用 DataContractAttribute 标记的类型。
支持使用 SerializableAttribute 标记或者实现 ISerializable 接口的类型。
实现 IXmlSerializable 接口的类型。
大多数集合(含泛型)类型,包括常用的 Array、List、IList 等。
KnownTypes
在OO中,对象继承是很常见的,但如果在WCF直接使用继承后,实际上被分成了2个独立的类。这时就需要使用KnownType来标识,这样在客户端生成代理后就变成继承的了。
还可以通过配置文件指定
4. 消息契约(Message Contract)
消息契约可以算是数据契约的一个分类,专为SOAP而生的。可以控制消息的格式。数据是放在Header 还是Body中。
非类型化(Untyped)
可以通过System.ServiceModel.Channels.Message类来直接构造消息,不过这个必须指定动作的地址,用来确定是那个操作被执行。 这样变可以不用构建类型了,不过会很累。。
非包装(Unwrapped)
WCF对消息序列化的时候,可以决定是否对消息进行包装。 如果,和其他系统整合的话,可能需要去掉包装,手动控制。
样式
可以通过设置XmlSerializerFormat的Style和Use可以使用样式
使用XMLReader
可以通过Message的GetReaderAtBodyContents 可以获取消息的XML部分
5. 错误契约(Fault Contract)
错误契约Fault Contract主要是来告诉一个服务或操作产生错误后,这个消息是什么样子的。 在SOA中,并没有限制客户端是什么平台,事实上,连服务端也是。只有契约没有变。
======================================华丽的分割线=================================================
在WCF中,契约分为四种,它们分别为:
这种级别的契约又包括两种:ServiceContract和OperationContract
ServiceContract用于类或者结构上,用于指示WCF此类或者结构能够被远程调用,而OperationContract用于类中的方法(Method)上,用于指示WCF该方法可被远程调用。
[ServiceContract]
public interface ICalculate
{
[OperationContract]
double Add(double a, double b);
}
数据契约也分为两种:DataContract和DataMember.DataContract用于类或者结构上,指示 WCF此类或者结构能够被序列化并传输,而DataMember只能用在类或者结构的属性(Property)或者字段(Field)上,指示WCF该属性或者字段能够被序列化传输。
我们还可以使用 DataContractAttribute、DataMemberAttribute 来标注自定义数据类型,这样我们就可以在服务方法中传递复杂的数据体了。使用之前,我们需要添加 System.Runtime.Serialization.dll 引用。由此我们可以看出其基本的开发模式,那就是使用 ServiceContract、OperationContract 执行运算,而使用 DataContract、DataMember 作为可序列化的数据载体。当然,我们也可以使用 "[Serializable]" 代替 "[DataContract]"。
[DataContract]
public class User
{
int _age = 27;
[DataMember]
public int Age
{
get { return _age; }
set { _age = value; }
}
string _userName = "wang.yq";
[DataMember]
public string UserName
{
get { return _userName; }
set { _userName = value; }
}
}
用于自定错误异常的异常契约:Fault Contract
FaultContract用于自定义错误异常的处理方式,默认情况下,当服务端抛出异常的时候,客户端能接收到异常信息的描述,但这些描述往往格式统一,有时比较难以从中获取有用的信息,此时,我们可以自定义异常消息的格式,将我们关心的消息放到错误消息中传递给客户端,此时需要在方法上添加自定义一个错误消息的类,然后在要处理异常的函数上加上FaultContract,并将异常信息指示返回为自定义格式。
简单的说,它能自定义消息格式,包括消息头,消息体,还能指示是否对消息内容进行加密和签名。
ServiceContract
OperationContract
DataContract
DataMember
契约是独立于平台的么?
WCF作为一种能够跨平台的体系框架,其应用肯定会有异构,异网的情况发生,那么作为通讯依据的契约能否自动适用于上述情况呢?答案是肯定的,契约是独立于平台之外的,它只约束通讯的双方应该遵守什么样的规则,而丝毫不管双方各自采用的是什么样的技术和什么样的操作系统,也只有这样,WCF才能有真正的生命力。
契约和以往哪种技术比较相像,又有什么不同?
如果非要拿契约和以往的技术相比较的话,契约和asp.net xml web service的声明性编程模型甚是相似,比如在web service中在类上标记WebServiceAttribute便可以将此类用于远程调用,而将方法添加WebMethondAttribute也可以将其暴露给远程客户端,这和WCF中的ServiceContract和OperationContract简直如出一辙,但不同的是,WCF中的契约要比Xml Web Service中的要详尽的多,比如ServiceContract和OperationContract可以直接使用在接口上面,而实现该接口的类就继承了这种契约声明,自动拥有契约所规范的动作和行为,这就使得程序员更方便的使用面向接口的编程方式,可以使同一服务拥有不同的实现,在新旧版本升级的同时,能够使新老版本共同运行。