一起学WCF【6】
来源:互联网 发布:openwrt php 环境搭建 编辑:程序博客网 时间:2024/05/01 17:27
服务契约和数据契约的版本控制
软件开发过程中需求是不断变化的,因此软件的功能和交换数据的结构也在变化。操作和数据的变化会引起WSDL文档的变化,因此产生服务的一个新版本。实际上从结构或技术上都不大可能连续地更新组件,不大可能对服务的每个变化都作出响应。
对于服务契约和数据契约,内容的修改不会引起客户端的不兼容,因此在它们发生变化时要保证交换信息的兼容性。
有些情况则需要添加新契约,如一个方法的名称发生变化,或需要添加一个数据成员时,这会导致契约的中断,因而需要创建一个新的版本。这样最好修改命名空间,使它适应新版本,然后再把服务托管在新的终结点上。
数据契约的版本控制
WCF和DataContractSerializer序列化器对数据契约的结构变化非常宽松。可以添加新的数据成员,或删除IsRequired属性为false的数据成员。这样做的一个优点是无须考虑不重要的数据类型。但是在面向服务的环境里,必须遵守契约,即当必须修改契约时,必须正式向外公开修改后的契约。
有以下结论:方法的参数可增可减;数据成员可增可减;数据类型可以改变,前提是必须保持兼容;可以增加方法。这些修改不会带来任何技术上的问题。
双向版本控制
一个经常出现的与数据契约版本控制有关的问题是新增特性,WCF可能会忽略把它反序列化为一个旧的数据契约。
现将前面的示例的功能完整表述如下:客户端向服务发送一个价格查询请求,并接收一个包含一个标志和价格的响应对象。客户端需要再次调用ConfirmPrice方法确认收到的价格。
接口部分的完整代码如下:
[ServiceContract]
publicinterface ICarRentalService
{
[OperationContract]
PriceCalculationResponseCalculatePrice(PriceCalculationRequest resp);
[OperationContract]
boolConfirmPrice(PriceCalculationRequest resp);
}
[DataContract]
publicclass PriceCalculationRequest
{
[DataMember(Name = "PickupDate", Order = 1, IsRequired =true)]
public DateTime PickupDate { get; set; }
[DataMember(Name = "ReturnDate", Order = 3)]
publicDateTime ReturnDate { get; set; }
[DataMember(Order = 2)]
publicstring PickupLocation { get; set; }
[DataMember(Order = 4)]
publicstring ReturnLocation { get; set; }
[DataMember]
privatestring VehicleType { get; set; }
publicstring Color { get; set; }
}
[DataContract]
publicclass PriceCalculationResponse
{
[DataMember]
publicint Flag { get; set; }
[DataMember]
publicstring Price { get; set; }
}
如果在对软件进行国际化时为数据添加一个Currency特性,它会在客户端丢失,因为客户端还不知道这个刚添加的特性,也没有办法对它的内容进行反序列化。
解决这个双向问题的方法是实现IExtensionDataObject接口并定义必不可少的ExtensionDataObject成员。如果DataContractSerializer序列化器在XML文档中检测到未知元素,在序列化时,会把它写入ExtensionDataObject属性包。当再欠引用ExtensionDataObject对象的内容时,它里面的内容还将保留,因此在数据契约的不同版本之间,数据不会丢失。
如果使用Add Service Reference对话框,数据类会自动实现此接口。如果希望把结果返回给服务器端,就必须在数据类中用手工方法实现此接口。示例代码如下:
[DataContract]
publicclass PriceCalculationResponse : IExtensibleDataObject
{
publicExtensionDataObject ExtensionData { get; set; }
[DataMember]
publicint Flag { get; set; }
[DataMember]
publicstring Price { get; set; }
}
服务契约版本控制的最佳实践
如果不同模式之间必须严格保持一致,则每个修改实现后,必须给契约一个新的版本。
如果不同模式之间没有必要严格保持一致,则需要注意:可以在任何时候添加新方法;可以不删除任何已存在的方法;参数的数据类型必须保持兼容。
数据契约版本控制的最佳实践
如果不同模式之间必须保持严格一致,则在每个修改实现后,必须赋给契约一个新的版本。
如果不同模式之间没有必要严格保持一致,需要注意:
不要由于继承的原因给数据契约赋予一个新版本,而是要创建一个独立的数据类;为了方便双向版本控制,必须一开始就实现IExtensionDataObject接口;如果确实需要改变数据类型或数据成员的名称,使用DataContract或DataMember特性生成兼容的数据结构;不要随便对数据类型进行修改;不要改变[DataMember(Order=?)]属性确定的数据成员的顺序;保持IsRequired的默认值(false)不变;可以在任何时候添加数据成员,但要注意会改变序列化的顺序。可把Order属性值设置为当前版本值;不要删除数据成员;不要对IsRequired属性做后续的修改。
消息契约
客户端与服务器之间需要交换SOAP信息。但到目前我们只是影响了SOAP体的内容。在大多数情况下,我们只能控制数据契约和操作契约。然而对于某些情形,需要对SOAP消息(包括消息体和消息头)进行控制。SOAP消息头正好适合这样的情形:需要传送额外的数据,不需要扩展数据类(如安全令牌)。
消息契约既可完全控制消息头的内容,也可控制消息体的结构。我们已经把数据契约看成是传递的参数和返回值,而它们完全可以替换为消息契约。如果选择消息契约则必须为所有参数使用消息契约,方法中不能混用数据结构和消息契约。
如下代码使用了[MessageContract]、[MessageHeader]和[MessageBody]这三个特性的用法:
[MessageContract]
publicclass PriceCalculation
{
[MessageHeader]
publicCustomHeader SoapHeader { get; set; }
[MessageBodyMember]
publicPriceCalculationRequest PriceRequest { get; set; }
}
[DataContract]
publicclass CustomHeader
{
[DataMember]
publicstring Username { get; set; }
}
除了增加一个传递价格请求对象外,还在SOAP消息头中传递了一个用户名。
XML序列化
WCF支持多种类型的序列化。默认使用DataContractSerializer类序列化CLR对象。前例已说到[DataContract]和[DataMember]特性的几个不同属性。在某种程度上,我们可控制生成的XML信息集的结构。
但对创建文档的影响而言DataContractSerializer类提供的功能较少,如不能满足要求我们可使用XML序列化器。
为了指示WCF使用XML序列化器,必须把自己的服务契约设置为[XmlSerializer-Format]特性,如果只是想某个操作使用XML序列化器,可把此特性应用于这个操作。默认时,XML序列化器会对所有公开的属性进行序列化。
如下例我们可以对PriceCalculationRequest类进行修改:
[XmlRoot("PriceRequest")]
publicclass PriceCalculationRequest
{
[XmlAttribute()]
publicstring PickupLocation { get; set; }
[XmlElement(ElementName="ReturnLoc")]
publicstring ReturnLocation { get; set; }
[XmlIgnore()]
publicDateTime PickupDate { get; set; }
privateDateTime ReturnDate { get; set; }
}
可对比生成的XSD文档查看效果。
- 一起学WCF【6】
- 一起学WCF【1】
- 一起学WCF【2】
- 一起学WCF【3】
- 一起学WCF【4】
- 一起学WCF【5】
- 那些年,我们一起学WCF--(6)PerCall实例行为
- 那些年,我们一起学WCF--(1)wcf初识
- 那些年,我们一起学WCF--(2)wcf服务契约
- 跟我一起从零开始学WCF系列课程
- 那些年,我们一起学WCF--(3)消息通信模式
- 那些年,我们一起学WCF--(4)数据契约
- 那些年,我们一起学WCF--(5)数据契约继承
- 那些年,我们一起学WCF--(7)PerSession实例行为
- 那些年,我们一起学WCF--(8)Single实例行为
- 那些年,我们一起学WCF--(9)会话行为
- 那些年,我们一起学WCF--(10)并发行为
- 那些年,我们一起学WCF--(11)自定义会话
- 记录学习stage3d时碰到的一个问题
- flashback--待完整
- 动画开源库:HMGLTransitions (WXHL) 3D动画切换, 开门效果等
- 基础加强学习之Java内省机
- Codeforces Round #136 (Div. 2) / 221B Little Elephant and Numbers (数论)
- 一起学WCF【6】
- 数据结构和算法分析
- POJ1835-模拟宇航员行走
- Learning Lua Programming (4) Cocos2d-x中Lua编程(一)
- create db时control/ redo logfile至少3个
- [leetcode] Binary Tree Inorder Traversal
- 一段送给自己的话--与君共勉
- (hdu 4586) Play the Dice
- shell 中哪些字符需要转意