第八讲:数据契约版本控制
代码
https://yunpan.cn/cPns5DkGnRGNs 密码:3913
数据契约是对用于交换的数据结构的描述,是数据序列化和反序列的依据。
在一个WCF应用中,客户端和服务端必须通过等效的数据契约进行有效的数据交换。随着时间的推移,不可避免的,我们会面临着数据契约版本的变化。比如数据成员的添加和删除等。
数据契约版本的差异最主要的表现形式是数据成员的添加和删除。如何保证在数据契约中添加一个新的数据成员,或者是从数据契约中删除一个现有的数据成员的情况下,还能保证现有客户端的正常服务调用(对于服务提供者),或者对现有服务的正常调用(针对服务消费者),这是数据契约版本控制要解决的问题。
例如:这个 场景描述
1 /// <summary> 2 /// 版本V1 3 /// </summary> 4 5 [DataContract] 6 public class CustomerV1 7 { 8 [DataMember] 9 public string Name { get; set; } 10 [DataMember] 11 public string PhoneNo { get; set; } 12 [DataMember] 13 public string Address { get; set; } 14 } 15 16 /// <summary> 17 /// 版本V2 18 /// </summary> 19 [DataContract] 20 public class CustomerV2 21 { 22 [DataMember] 23 public string Name { get; set; } 24 [DataMember] 25 public string PhoneNo { get; set; } 26 27 }
数据契约成员的移除导致在发送—回传过程中数据的丢失问题。客户端基于数据契约CustomerV1进行服务调用,而服务的实现却是基于CustomerV2的,那么序列化的CustomerV1对象生成的XML通过消息传到服务端,服务端会按照CustomerV2进行反序列化,毫无疑问Address的数据会丢失。如果Customer的信息需要返回到客户端,服务需要对CustomerV2对象进行序列化,则序列化生成的XML肯定没有Address数据成员存在。当回复消息返回到客户端时,客户端按照CustomerV1进行反序列化生成CustomerV1对象,会发现原本赋了值的Address属性现在变成了null。
对于客户端来说,这是一件怪事也是不可接受的事情:“为何数据经过发送—回传后会无缘无故的丢失呢”?
为了解决这个问题
WCF定义了一个特殊的接口System.Runtime.Serialization.IExtensibleDataObject,该接口中仅仅定义了一个ExtensionDataObject类型属性成员,对于实现了IExtensibleDataObject的数据契约,WCF在进行序列化时会将ExtensionData属性的值也序列化到XML;在反序列化过程中,如果发现XML包含有数据契约中没有的数据,会将多余的数据进行反序列化,并将其放入ExtensionData属性中保存起来,由此解决数据丢失的问题。
我们可以测试一下,根据第六讲的 1.数据契约基本的使用 的 代码 进行 测试。
打开 ContentTypes 项目下的 ListItem 数据契约类,注释在该类的最后一个属性 Url
[ 8-01 ]
然后我们将整个项目 重新生成一遍,然后 启动 Hosting,启动WinFrom(这里先不不要 更新服务引用 ) ,在文本框中输入 响应的 字符串
点击 保存 按钮,最后点击 获取 按钮。
这个时候你会发现 你刚才输入的 网站 的 文本框中 的 字符串消失了。
这是因为服务器 是按照 你刚才改动的 数据契约 进行响应的处理,而客户端 再你没有 更新服务引用的情况下 ,是用的 之前的 数据契约 进行 传递数据的 , 之前的 数据契约 有 Url 这个属性,但是 服务器 修改后 的 服务契约 中是 没有 Url 这个属性的。
那客户端 我传了 Url 的值,凭什么 返回来 就没有数据了?难道 你服务器 版本更新了,我就也要 进行版本更新? 那 万一 别的很多已经下载过 你的软件,而因为你的服务器更新,就不能用了吗?这样肯定不行。
这样说把,服务器版本更新了,无非就是 数据契约 新增了 属性 或者 删除了属性。 那我客户端没有更新,客户端的版本比服务器版本低。 客户端 传了 某个 服务器已经删除的 属性,服务器你不管,忽略就是了,但是 服务器回传的 时候 一定也要把 我这个被你忽略的 属性 值 给 原样返回就是了。
同理,如果服务器要是增加了 新的属性,我客户端是老版本没有这个最新的数据契约的 属性,那客户端不知道有这个属性,就不传,服务端的这个属性会是个NULL 。
如何做呢?
让我们的 数据契约 去继承 IExtensibleDataObject 这个接口,并且实现该接口
[ 8-02 ]
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Runtime.Serialization; 6 7 namespace ContentTypes 8 { 9 [DataContract(Namespace = "http://www.HulkXu.com/")] 10 //[DataContract] 11 public class ListItem : IExtensibleDataObject 12 { 13 [DataMember(Name = "Id", IsRequired = false, Order = 0)] 14 //[DataMember] 15 private long m_id; 16 17 public long Id 18 { 19 get { return m_id; } 20 set { m_id = value; } 21 } 22 //[DataMember] 23 [DataMember(Name = "Title", IsRequired = true, Order = 1)] 24 private string m_title; 25 26 public string Title 27 { 28 get { return m_title; } 29 set { m_title = value; } 30 } 31 //[DataMember] 32 [DataMember(Name = "Description", IsRequired = true, Order = 2)] 33 private string m_description; 34 35 public string Description 36 { 37 get { return m_description; } 38 set { m_description = value; } 39 } 40 //[DataMember] 41 [DataMember(Name = "DateStart", IsRequired = true, Order = 3)] 42 private DateTime m_dateStart; 43 44 public DateTime DateStart 45 { 46 get { return m_dateStart; } 47 set { m_dateStart = value; } 48 } 49 //[DataMember] 50 [DataMember(Name = "DateEnd", IsRequired = false, Order = 4)] 51 private DateTime m_dateEnd; 52 53 public DateTime DateEnd 54 { 55 get { return m_dateEnd; } 56 set { m_dateEnd = value; } 57 } 58 59 60 //删除 61 //[DataMember] 62 //[DataMember(Name = "Url", IsRequired = false, Order = 5)] 63 //private string m_url; 64 65 //public string Url 66 //{ 67 // get { return m_url; } 68 // set { m_url = value; } 69 //} 70 71 72 //添加 73 //[DataMember(IsRequired=true)] 74 //private string address; 75 //public string Address 76 //{ 77 // get { return address; } 78 // set { address = value; } 79 //} 80 //public ExtensionDataObject ExtensionData 81 //{ 82 // get; 83 // set; 84 //} 85 86 87 public ExtensionDataObject ExtensionData 88 { 89 get; 90 set; 91 } 92 93 94 } 95 }
好的 ,关于 数据契约版本控制 就是这些了,比较简单。