WCF 第二章 契约 数据契约版本
2011-06-23 21:51 DanielWise 阅读(907) 评论(0) 编辑 收藏 举报变化是不可避免的。企业改变,技术改变,法律改变,软件契约也会改变。在面对软件的变更时,一个坚实的版本控制是必须的。我们必须为不可避免的变化做好提前准备同时对已经存在的客户端进行向后兼容处理。
对数据契约版本控制来说,最常见的需求是向已有的数据契约中添加成员。通过这一部分的描述,你可以做任何改动而不会破坏现有客户端。但是如果你需要打破现有客户端的向后兼容性,你必须通过改变数据契约的名字或者命名空间来定义另一个版本的数据契约。
一个小的注意是要注意不间断的变化。不间断,从WCF的标准来看,可能会打破与其他系统的兼容性。例如,如果与一个要求元数据验证的系统通信,系统可能会拒绝消息如果接收到的XML实例中有不可预期的元素。这一章中提到的不间断周期是指那些不会影响WCF到WCF通信的改变。
不间断改变
两种类型的改变将不会打破现有客户端的兼容性
1. 添加新的非必须数据成员
2. 移除现有的非必须的数据成员
在这两种情况中,可以通过简单的忽略新增的或遗失的非必须数据元素来从新消息中创建一个原有的类型。相反的,也可以从原有类型中创建新消息。DataContractSerializer将会在运行时自动处理这些。
间断的改变
尽管你可以在数据契约中改变特定的水洗那个而且保证向后兼容性,非常多的元素改变将影响现有的客户端。如果你对一个数据契约定义做了以下改动中的任何一种,当前的客户端将不再会继续正常工作。
1. 改变一个数据契约的名字或命名空间
2. 对先前是必须的数据成员进行重命名操作
3. 添加一个新的数据成员而且它的名字与之前用过的一样
4. 改变一个已经存在的数据成员的数据类型
5. 在DataMemberAttribute上添加IsRequired=true的新成员
6. 在DataMemberAttribute上移除IsRequired=true的新成员
列表2.24 显示了两种数据契约定义: 第一个定义在服务V1中,第二个定义在V1服务的另一个版本V2中。注意V1和V2的区别,数据成员Currency被移除同时DailyVolume被添加进来。这个改变时不间断的。
列表2.24 一个数据契约的不间断改变-添加并且删除数据成员
不间断改变
两种类型的改变将不会打破现有客户端的兼容性
1. 添加新的非必须数据成员
2. 移除现有的非必须的数据成员
在这两种情况中,可以通过简单的忽略新增的或遗失的非必须数据元素来从新消息中创建一个原有的类型。相反的,也可以从原有类型中创建新消息。DataContractSerializer将会在运行时自动处理这些。
间断的改变
尽管你可以在数据契约中改变特定的水洗那个而且保证向后兼容性,非常多的元素改变将影响现有的客户端。如果你对一个数据契约定义做了以下改动中的任何一种,当前的客户端将不再会继续正常工作。
1. 改变一个数据契约的名字或命名空间
2. 对先前是必须的数据成员进行重命名操作
3. 添加一个新的数据成员而且它的名字与之前用过的一样
4. 改变一个已经存在的数据成员的数据类型
5. 在DataMemberAttribute上添加IsRequired=true的新成员
6. 在DataMemberAttribute上移除IsRequired=true的新成员
列表2.24 显示了两种数据契约定义: 第一个定义在服务V1中,第二个定义在V1服务的另一个版本V2中。注意V1和V2的区别,数据成员Currency被移除同时DailyVolume被添加进来。这个改变时不间断的。
列表2.24 一个数据契约的不间断改变-添加并且删除数据成员
[DataContract(Namespace="http://EssentialWCF")]
publicclass StockPrice //V1
{
[DataMember] publicdouble CurrentPrice;
[DataMember] public DateTime CurrentTime;
[DataMember] publicstring Ticker;
[DataMember] publicstring Currency;
}
[DataContract(Namespace ="http://EssentialWCF")]
publicclass StockPrice //V2
{
[DataMember] publicdouble CurrentPrice;
[DataMember] public DateTime CurrentTime;
[DataMember] publicstring Ticker;
[DataMember] publicint DailyVolume;
}
publicclass StockPrice //V1
{
[DataMember] publicdouble CurrentPrice;
[DataMember] public DateTime CurrentTime;
[DataMember] publicstring Ticker;
[DataMember] publicstring Currency;
}
[DataContract(Namespace ="http://EssentialWCF")]
publicclass StockPrice //V2
{
[DataMember] publicdouble CurrentPrice;
[DataMember] public DateTime CurrentTime;
[DataMember] publicstring Ticker;
[DataMember] publicint DailyVolume;
}
对现有的客户端来说,想要在新数据成员添加进来以后仍然进行正常的数据传输,原始数据契约必须支持扩展性。就是说,原始数据契约必须支持对将来的未知的数据的序列化。这就实现了回转,一个客户端可以把V2的数据发送给V1,然后把V1再重新发回给V2。WCF默认通过由svcutil.exe 生成的代理代码实现扩展性。如果你不想支持这个特性,它可以通过在服务配置文件中的ServiceBehavior部分加上<dataContractSerializer ignoreExtensionDataObject=”true”/>来禁用。
列表2.25显示了调用GetPrice来获取一个StockPrice对象并把这个对象发送给StoreStockPrice的客户端代码。假设通过svcutil.exe生成的StockService的代理指向V1服务,然后这个服务被更新为列表2.24中的V2服务。当客户端运行V2服务时,GetPrice方法将会放回有DailyVolume成员被添加同时Currency对象被移除的XML。数据契约反序列化器知道V1 StockPrice对象,会把DailyVolume成员放到V1对象的ExtensionData成员中而且不会提示丢失Currency成员。客户端代码将会接受预期的StockPrice对象,仅会发现Currency被初始化为默认值而且总体对象将会有点”重”。这是因为额外的扩展数据(DailyVolume)在类中可见。这种方式下,服务发送合法的V2数据同时客户端使用由哪些数据组成的合法的V1服务。
列表2.25 使用一个V1契约调用一个V2服务
列表2.25显示了调用GetPrice来获取一个StockPrice对象并把这个对象发送给StoreStockPrice的客户端代码。假设通过svcutil.exe生成的StockService的代理指向V1服务,然后这个服务被更新为列表2.24中的V2服务。当客户端运行V2服务时,GetPrice方法将会放回有DailyVolume成员被添加同时Currency对象被移除的XML。数据契约反序列化器知道V1 StockPrice对象,会把DailyVolume成员放到V1对象的ExtensionData成员中而且不会提示丢失Currency成员。客户端代码将会接受预期的StockPrice对象,仅会发现Currency被初始化为默认值而且总体对象将会有点”重”。这是因为额外的扩展数据(DailyVolume)在类中可见。这种方式下,服务发送合法的V2数据同时客户端使用由哪些数据组成的合法的V1服务。
列表2.25 使用一个V1契约调用一个V2服务
localhost.StockServiceClient proxy =new localhost.StockServiceClient();
localhost.StockPrice s = proxy.GetPrice("msft");
proxy.StoreStockPrice(s);
localhost.StockPrice s = proxy.GetPrice("msft");
proxy.StoreStockPrice(s);
作者:DanielWise
出处:http://www.cnblogs.com/danielWise/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。