数据契约发布于服务元数据中,服务元数据允许客户端将与平台,技术无关的数据类型表示形式转换为客户端本地的表现形式。
事实上,将对象(或值类型)作为操作参数进行传递时,真正需要发送的是对象的状态,然后接收端再将它转换为本地的表示形式。这种传递方式称为值编组。执行按值编组的最简单办法是利用大多数平台自身提供的序列化技术。
一个类如果是可序列化的,则.Net要求他的所有成员变量都要支持序列化。
.Net提供了两种格式器:BinaryFormatter序列化为二进制格式;SoapFormatter序列为SOAPXML格式。
这样的序列化虽然足够使用了,但是用在客户端与服务之间面向服务的交互却还不够理想。
所以WCF提供了新的面向服务特性DataContract。DataContract特性可用于枚举,结构体,类。
[DataContract] class Person { private string Name; [DataMember] public string MyName { get { return Name; } set { Name = value; } } }
注意,类Person加入了DataContract特性,只是将类型参与到数据契约中,以指示类型可以被按值封送。如果要序列化类型成员,就必须使用DataMemberAttribute特性,它可用在字段和属性上,但是我们建议只用在属性上,且不要在字段和属性上同时定义DataMember特性。同样,类的访问性对于WCF没有任何影响。
当DataMember特性应用到属性上时,该属性必须具有get,set访问器。因为WCF会在序列化和反序列化时使用该属性。
此外,WCF还提供了数据契约的推断,就是我们没有为Person定义DataContract特性,但是在WCF的传输中应用到了此类,那么WCF就默认为我们的Person类加上了DataContract特性,并且成员都加上了DataMember特性,为了学习,所以建议大家都自己定义数据契约,不要使用WCF数据契约推断。
[DataContract] class Person { private string Name; [DataMember] public string MyName { get { return Name; } set { Name = value; } } } [DataContract] class School { private string SchoolName; [DataMember] public string MySchoolName { get { return SchoolName; } set { SchoolName = value; } } [DataMember] Person student { get; set; } }
这就是组合契约,本身就是数据契约的成员,我们仍然可以把他标记为DataMember特性。在服务发布的时候,会一同发布。
[DataContract] class Person { private string Name; [DataMember] public string MyName { get { return Name; } set { Name = value; } } [OnSerializing] void OnSerializing(StreamingContext context) { } [OnSerialized] void OnSerialized(StreamingContext context) { } [OnDeserializing] void OnDeserializing(StreamingContext context) { } [OnDeserialized] void OnDeserialized(StreamingContext context) { } }
还有一点需要注意,反序列化期间,是不会调用构造函数的,但是可以使用事件来完成。
[ServiceContract] interface IContractManager { //不能接受Customer对象 [OperationContract] void AddContract(Contract contract); //不能返回Customer对象 [OperationContract] Contract[] GetContracts(); } [DataContract] public class Contract { private string _firstName; [DataMember] public string FirstName { get { return _firstName; } set { _firstName = value; } } } [DataContract] class Customer : Contract { private int _customerNumber; [DataMember] public int CustomerNumber { get { return _customerNumber; } set { _customerNumber = value; } } }
解决以上问题的方法是使用KnownTypeAttribute特性明确地告知WCF关于Customer类的信息
[ServiceContract] interface IContractManager { //不能接受Customer对象 [OperationContract] void AddContract(Contract contract); //不能返回Customer对象 [OperationContract] Contract[] GetContracts(); } [DataContract] [KnownType(typeof(Customer))] public class Contract { private string _firstName; [DataMember] public string FirstName { get { return _firstName; } set { _firstName = value; } } } [DataContract] class Customer : Contract { private int _customerNumber; [DataMember] public int CustomerNumber { get { return _customerNumber; } set { _customerNumber = value; } } }
枚举类型的定义总是支持序列化的。当定义一个新枚举时,不必应用DataContract特性,就可以在数据契约中使用它。但是,如果要排除枚举中的某一项,那么就要使用DataContract和EnumMember
[DataContract] enum ContractType { [EnumMember] Customer, [EnumMember] Vendor, //不会成为数据契约的一部分 Partner }