WCF技术剖析之十六:数据契约的等效性和版本控制2012-11-03 博客园 Artech数据契约是对用于交换的数据结构的描述,是数据序列化和反序列化的依据。在一个WCF应用中,客户端和服务端必须通过等效的数据契约方能进行有效的数据交换。随着时间的推移,不可避免地,我们会面临着数据契约版本的变化,比如数据成员的添加和删除、成员名称或者命名空间的修正等,如何避免数据契约这种版本的变化对客户端现有程序造成影响,就是本节着重要讨论的问题。
一、数据契约的等效性数据契约就是采用一种厂商中立、平台无关的形式(XSD)定义了数据的结构,而WCF通过DataContractAttribute和DataMemberAttribute旨在给相应的类型加上一些元数据,帮助DataContractSerializer将相应类型的对象序列化成具有我们希望结构的XML。在客户端,WCF的服务调用并不完全依赖于某个具体的类型,客户端如果具有与服务端完全相同的数据契约类型定义,固然最好。如果客户端现有的数据契约类型与发布出来数据契约具有一些差异,我们仍然可以通过DataContractAttribute和DataMemberAttribute这两个特性使该数据契约与之等效。简言之,如果承载相同数据的两个不同数据契约类型对象最终能够序列化出相同的XML,那么这两个数据契约就可以看成是等效的数据契约。等效的数据契约具有相同的契约名称、命名空间和数据成员,同时要求数据成员出现的先后次序一致。比如,下面两种形式的数据契约定义,虽然它们的类型和成员命名不一样,甚至对应成员在各自类型中定义的次序都不一样,但是由于合理使用了DataContractAttribute和DataMemberAttribute这两个特性,确保了它们的对象最终序列化后具有相同的XML结构,所以它们是两个等效的数据契约。
1: [DataContract(Namespace = "http://www.artech.com/")]
2: public class Customer
3: {
4: [DataMember(Order=1)]
5: public string FirstName
6: {get;set;}
7:
8: [DataMember(Order = 2)]
9: public string LastName
10: { get; set; }
11:
12: [DataMember(Order = 3)]
13: public string Gender
14: { get; set; }
15: }
1: [DataContract(Name = "Customer", Namespace = "http://www.artech.com/")]
2: public class Contact
3: {
4: [DataMember(Name = "LastName", Order = 2)]
5: public string Surname
6: { get; set; }
7:
8: [DataMember(Name = "FirstName", Order = 1)]
9: public string Name
10: { get; set; }
11:
12: [DataMember(Name = "Gender", Order = 3)]
13: public string Sex
14: { get; set; }
15: }
数据契约版本的差异最主要的表现形式是数据成员的添加和删除。如何保证在数据契约中添加一个新的数据成员,或者是从数据契约中删除一个现有的数据成员的情况下,还能保证现有客户端的正常服务调用(对于服务提供者),或者对现有服务的正常调用(针对服务消费者),这是数据契约版本控制需要解决的问题。
二、数据成员的添加先来谈谈添加数据成员的问题,如下面的代码所示,在现有数据契约(CustomerV1)基础上,在服务端添加了一个新的数据成员: Address。但是客户端依然通过数据契约CustomerV1进行服务调用。那么,客户端按照CustomerV1的定义对于Customer对象进行序列化,服务端则按照CustomerV2的定义对接收的XML进行反序列化,会发现缺少Address成员。那么在这种数据成员缺失的情况下,DataContractSerializer又会表现出怎样的序列化与反序列化行为呢?
1: [DataContract(Name = "Customer", Namespace = "http://www.artech.com")]
2: public class CustomerV1
3: {
4: [DataMember]
5: public string Name
6: { get; set; }
7:
8: [DataMember]
9: public string PhoneNo
10: { get; set; }
11: }
1: [DataContract(Name = "Customer", Namespace = "http://www.artech.com")]
2: public class CustomerV2
3: {
4: [DataMember]
5: public string Name
6: { get; set; }
7:
8: [DataMember]
9: public string PhoneNo
10: { get; set; }
11:
12: [DataMember]
13: public string Address
14: { get; set; }
15: }