WCF中的数据契约(Continued)

摘要:
版本化的数据契约更改是不可避免的。DataContractSerializer将在运行时自动完成这些操作。注意,在V1和V2之间,数据成员Currency被删除,DailyVolume被添加。已知V1的StockPrice对象的反序列化程序将替换对象的ExtensionData字段中的DailyVolume成员。这是因为额外的扩展数据在类中也可用。如果两个类是序列化的,并且具有相同的XML结构,则数据契约假定这两个类相等。默认情况下,服务和WCF中的契约将按字母顺序排列。
版本化数据契约

变化是不可避免的。业务的改变,技术的改变,规则的改变,这些都会造成软件契约的变化。面对软件的变化,一个稳定的版本策略是必不可少的。必须注意对不可避免的变化进行预先的筹划,并对当前的客户端保证向后的兼容性。

最常见的对版本化契约的需求是,当现有数据契约中添加新的成员时。按照不间断的版本更改的描述,你可以自由更改,而不需要终止现有的客户端。但如果你需要终止向后兼容的现有客户端,就必须版本化整个数据契约或命名空间。

对于不间断的更新,有一点我们必须小心。不间断,从WCF的角度来看,很可能会破坏与其它系统之间的兼容性。例如,如果一个通信系统,需要严格的结构验证,那么如果该系统收到包含意想不到的元素的XML实例,该系统可能会拒绝这个消息。术语“不间断”,在这里的意思是说执行对数据契约的更改时可以不影响WCF的正常通信。

不间断更改

两种类型的修改将不会中断与现有客户端的兼容性:

  • 添加新的非必须的数据成员
  • 移出现有的非必须的数据成员

在这两种情况下,有可能在一个新的消息中简单的忽略新的数据成员或丢失非必须的数据来创建一个旧的类型。相反,它也可能从旧的类型创建一个新的类型。DataContractSerializer会在运行时,自动完成这些操作。

间断更改

虽然你可以更改向后兼容的数据契约中的某些属性,但是,许多项目的变化会间断现有的客户端。如果你对数据契约做出任何改变,那么现有客户端可能不再正常工作:

  • 改变数据契约的名字或命名空间。
  • 重命名以前必须的数据成员。
  • 以已使用的名称添加一个新的数据成员。
  • 改变现有数据成员的数据类型。
  • 添加的新成员的DataMember属性中定义了IsRequired=true。
  • 要移出的新成员的DataMember属性中定义了IsRequired=true。

下面代码显示了两个数据契约的定义:第一个定义在V1服务上,第二个定义在V2服务上。注意,在V1和V2之间,数据成员Currency被移出,而DailyVolume被添加上来。这种改变是非中断的

   1:  namespace WcfServiceLibraryDataContract
   2:  {
   3:      [DataContract(Namespace = "http://WcfServiceLibraryDataContract")]
   4:      public class clsStockPrice:clsPrice //V1
   5:      {
   6:          [DataMember]
   7:          public string Ticker;
   8:          [DataMember]
   9:          public string Currency;
  10:      }
  11:  }
   1:  namespace WcfServiceLibraryDataContract
   2:  {
   3:      [DataContract(Namespace = "http://WcfServiceLibraryDataContract")]
   4:      public class clsStockPrice:clsPrice //V2
   5:      {
   6:          [DataMember]
   7:          public string Ticker;
   8:          [DataMember]
   9:          public long DailyVolume;
  10:      }
  11:  }

对于现有的客户端,要想在新的数据成员改编后,仍可传递正确的数据,那么原始的数据契约必须支持扩展性。也就是说,原始的数据必须支持对未知类型的序列化。也就是说,客户端传递V2的数据到V1服务上,并且V1的服务返回V1的数据到V2的客户端时,元素依然完好。WCF可以通过svcutil.exe生成的默认的代理代码来实现扩展性。如果你不想支持此功能,可以通过在ServiceBehavior配置节中定义<dataContractSerializer ignoreExtensionDataObject=”true”/>来禁用它。

下面的代码显示了客户端调用GetPrice来获得StockPrice对象,然后传递这个对象到StoreStockPrice。假设,StockService代理是使用svcutil.exe生成的指向V1 服务的。然后这个服务被更改为V2。当客户端针对V2服务运行时,GetPrice将会以DailyVolume成员返回XML,并删除其中的Currency成员。已知V1的StockPrice对象的反序列化器,会在对象的ExtensionData字段中替换DailyVolume成员。并且,不在意丢失的Currency成员。客户端代码将会获取到期望的StockPrice对象,却发现它包含Currency的默认值,而且整个对象有些“重”。这是因为额外扩展的数据(DailyVolume)在类中也是可用的。这样,服务可以传递有效的V2数据,并且客户端预定的是一个有效的V1数据的表现形式。

   1:  localhost.StockServiceClient proxy = new localhost.StockServiceClient()
   2:  localhost.StockPrice s = proxy.GetPrice("Chinasofti");
   3:  proxy.StoreStockPrice(s);
数据契约的等价

如果你使用WCF来暴露服务以及使用svcutil.exe来生成服务的访问者代理,你通常不需要关心客户端和服务端之间传递的消息的表现形式。数据契约直接由WCF序列化.NET类型到XML结构中,并且反序列化一个XML结构回.NET类型。XML信息在通信时可能在通信线路上编码为文本或二进制形式。但同样的,.NET代码也不知道编码形式。这样,在代码中操作.NET类型,而在线路上传递的却是基于标准XML信息的编码表现形式。

但是,有些情况下,你可能在服务端和客户端使用不同的类型。如果客户端和服务端由不同的机构开发出来,或者只有一方的通信使用WCF时,这种情况可能发生。事实上,如果你不使用svctuil.exe或者添加服务引用来在客户端生成代理类,这就有机会产生客户端的成员名和服务器端的成员名不同的情况。但是通过使用[DataMember]属性控制这些名称,使他们在XML的表现形式中保持一致。只要客户端和服务端使用相同的XML表现形式,WCF就不会反序列化XML信息为不同的.NET类型。如果两个类序列化后,是相同的XML结构,数据契约就认为这两个类是相等的。对于数据契约的相等来说,他们必须有相同的命名空间和成员。数据成员也必须是相同的类型,在XML中也必须具有相同的顺序。总之,他们必须没有任何差别。下面代码显示了两个相等的数据契约,第一个契约被暴露在服务中,第二个契约是由客户端描述的。两个契约在生成XML结构后是相等的。默认情况下,服务中的契约,WCF将会按照字母的顺序排列。因为DataContract属性和DataMember中Name=”StockPriceSvc” 和Name=“Currency”属性的定义,第二个契约生成XSD后与第一个契约完全一致。

   1:  [DataContract(Namespace = "http://WcfServiceLibraryDataContract")]
   2:      public class clsStockPriceSvc
   3:      {
   4:          [DataMember]
   5:          public double CurrentPrice;
   6:          [DataMember]
   7:          public DateTime CurrentTime;
   8:          [DataMember]
   9:          public string Currency;
  10:          [DataMember]
  11:          public string Ticker;
  12:   
  13:      }
   1:  [DataContract(Namespace = "http://WcfServiceLibraryDataContract", Name="clsStockPriceSvc")]
   2:      public class clsStockPrice
   3:      {
   4:          [DataMember(Order=4)]
   5:          public string Ticker;
   6:          [DataMember(Order=2)]
   7:          public double CurrentPrice;
   8:          [DataMember(Order=3)]
   9:          public DateTime CurrentTime;
  10:          [DataMember(Order=1,Name="Currency")]
  11:          public string Money;
  12:      }

使用集合

集合是在.NET中是结合了动态内存分配、枚举、以及列表导航等优点的非常方便的数据结构。虽然有用,但没有XSD或WSDL与集合相等。因此,为了序列化一个集合到XML,WCF将他们视为数组。事实上,在线路级别的序列化上,集合与数组是相同的。除了集合(实现了ICollection<T>的类型),实现IEnumerable<T>或IList<T>的类型也是如此处理。

下面代码显示了一个使用集合的服务契约的一个操作。集合以[CollectionDataContract]属性修饰。这个属性专门为处理集合而定义。这个属性告诉WCF实例化任何支持IEnumerable和以及在数组中实现Add方法的类型。StockPriceCollection类继承自List泛型,这个泛型基于ICollection接口。所以,这个类型可以被序列化。

   1:      [DataContract(Namespace = "http://WcfServiceLibraryDataContract")]
   2:      public class clsStockPrice
   3:      {
   4:          [DataMember]
   5:          public double CurrentPrice;
   6:          [DataMember]
   7:          public DateTime CurrentTime;
   8:          [DataMember]
   9:          public string Currency;
  10:          [DataMember]
  11:          public string Ticker;
  12:   
  13:      }
   1:      [CollectionDataContract]
   2:      public class clsStockPriceCollection: List<clsStockPrice>
   3:      {
   4:      }
   1:      [ServiceContract(Namespace = "http://WcfServiceLibraryDataContract/FinanceService/")]
   2:      public interface IStockService
   3:      {
   4:          [OperationContract]
   5:          clsStockPriceCollection GetPricesAsCollection(string[] tickers);
   6:      }
   1:      [ServiceBehavior(Namespace = "http://WcfServiceLibraryDataContract/FinanceService/")]
   2:      public class StockService : IStockService
   3:      {
   4:          #region IStockService Members
   5:   
   6:          clsStockPriceCollection GetPricesAsCollection(string[] tickers)
   7:          {
   8:              clsStockPriceCollection list = new clsStockPriceCollection();
   9:              for (int i = 0; i < tickers.GetUpperBound(0) + 1; i++)
  10:              {
  11:                  clsStockPrice p = new clsStockPrice();
  12:                  p.Ticker = tickers[i];
  13:                  p.CurrentPrice = 94.85;
  14:                  p.CurrentTime = DateTime.Now;
  15:                  list.Add(p);
  16:              }
  17:              return list;
  18:          }
  19:   
  20:          #endregion
  21:      }

免责声明:文章转载自《WCF中的数据契约(Continued)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇HTML5 canvas游戏工作原理OpenCV 图像采样 插值 几何变换下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

WCF NetTcpBinding 由于系统缓冲区空间不足或队列已满,不能执行套接字上的操作

背景:WindowsService + WCF + NetTcpBinding 之前一直使用http协议模式,改为net.tcp之后隔段时间出现:由于系统缓冲区空间不足或队列已满,不能执行套接字上的操作。 127.0.0.1:9000 记录时间:2016-01-14 10:02:58日志级别:Exception 日志位置:CloudTraPlatSOA....

thinkphp6事件监听和触发多个事件

thinkphp6事件订阅,监听多个事件 创建一个事件类 创建一个监听类 修改配置文件确定触发事件 创建一个事件类 php think make:event User <?php namespace appevent; class User { public function __construct() {...

Dapr组件(Components)

前言: 前面文章对Dapr的构建块进行了解,接下来对Dapr中对构建块的实现——组件 一、组件 Dapr 使用模块化设计,将功能作为组件来提供。 每个组件都有接口定义。 所有的组件都是可插拔的,因此在理想的情况下,你可以用一个具有相同接口的组件换掉另一个。 用于生产的每个组件, 需要保持一套技术要求,以确保组件的功能兼容性和强度。 组件需包含: 符合定义...

Nacos环境隔离 namespace

nacos服务分级存储模型 namespace:相当于环境,开发环境  测试环境  生产环境 ,每个空间里面的配置是独立的 (默认是 public) group:这个类似我们的项目,比如 风控系统 ,交易系统, 就是不同的项目 创建namespace,用来隔离不同环境 配置application.yml spring.cloud.nacos.dis...

WCF学习笔记1(体系架构和行为扩展)

引用《WCF服务编程》里的一段话:“以WCF为基础框架搭建面向服务的企业级应用程序,以WF工作流引擎支撑企业应用中业务流程的传递与控制,以Cardspace和WCF固有的安全测罗保证企业信息的安全,最后以ASP.NET AJAX,WPF和SILVERLIGHT技术丰富客户端界面的绚丽表现,从而改善企业客户对应用程序的体验,这就是微软实现企业级应用的霸业宏图...

【转载】通过服务端监控结果,说说WCF的并发处理

InstanceContextMode表示的是,WCF允许产生可用来处理包含在传入消息中的调用的服务(契约类)的实例InstanceContext模式数,WCF的并发模式ConcurrencyMode是针对某个封装了服务实例的InstanceContext而言的 简单的说,InstanceContextMode表示产生多少个服务实例对象,Concurren...