在我们看来Service Orientation提供了一种对业务功能进行分解的方式针对SO我们把一个具体的业务流程或者一个复杂的功能分解成一个个独立完成某项任务的子单元这些子单元通过一个个Service来承载对于Service本身来讲他们应该是自治的独自完成自己的功能不依赖于其他的Service但是Service的价值体现在它被潜在的消费者使用的程度这实际上包含两方面的内容作为Service本身它如何将自己暴露出来供一切可能的潜在用户调用这些潜在用户不仅仅指那些不同的Client也包含其他的ServiceService Orientation其中一个特征就是Service should be composite鼓励将一个个相关细粒度的Service组合成一个大的Service这样有利于较大限度的实现重用而重用往往意味着更小的投入更佳的可维护性而另一方面就是这些消费者通过怎样的方式来调用它所需要的Service 这实际上体现了两者相互交互的问题在一个分布式的环境中要实现两者的交互有两个必须要解决的问题如何保证Service的使用者对Service的调用能够被Service端理解以及对Service的调用如何抵达Service Side后者实质上是关于communication的问题我们现在不去谈它第一个问题就是Contract需要解决的问题 我们知道SOA一个主要的目标就是促进不同技术平台的互操作要真正实现这样一个宏伟的目标是一件极不容易的事情需要不同的厂商和标准组织相互协作制定一个大家一致遵循的标准这样一个标准就是WS* 我们很清楚无论个个厂商各自的标准怎样千差万别但是有个标准是他们必须要遵循的那就是Internet的标准如果哪家公司拒绝Internet那肯定要被淘汰的而对于Internet基于Http的网络协议和基于XML的数据表达已经成为了事实上的标准对于SOA来说XML不仅仅用于表示Service调用携带的数据(参数和返回值)更用于表示这个调用本身以及满足各种要求的控制信息 比如基于SecuritySessionReliable MessagingTransaction等等的控制信息WS*就是一个基于XML的标准而对于SOA中的Contract所要做的就是寻求一种厂商中立的方式来表示Service的接口和用于交互的数据结构前者就是Service Contract后者就是Data Contract SOA中的一个Service由一组相关的Operation来构成Service Contract用于表示构成该Service所有Operation的Interface(而不是Implementation)说得更加具体点大家都知道Consumer和Service之间的交互都是通过Message的形式来实现的一次交互就是一次Message Exchange在不同的场景我们以不通过Pattern来进程Message Exchange比如我们通常使用RequestResponse的方式来向Service发送Request进而得到返回结果我们也可以以RequestForget的形式来异步地调用Service(不需要从Service获取Response)我们可以让一个Service在没有收到任何Request的情况下以广播的形式向注册的Client发送通知当然我们还有其他不同的消息交互的模式我们把这些不同的信息交互方式称为MEP(Message Exchange Pattern)也就是说一个Operation最终通过被最终转换成了按照某种MEP进行的消息交互而Service Contract旨在实现对这种MEP的描述比如是否需要Request Message或者Response Message(如果仅仅有Response Message就是Notification的方式如果仅仅具有Request Message那就是我们上面谈到的RequestForget的模式)和Message本身具有的格式 上面我们说了Service Contract是以一种厂商中立的形式描述体现为某种模式的消极交互构成整个Service的所有Operation而我们也说了Consumer和Service的交互本质上看就是按照某种Pattern体现的一次Message Exchange好像具有了Service Contract的描述就可以了但是实际上单单有了Service Contract对Service的描述还不够因为Service Contract本身缺乏对携带于Message用于信息传递的数据类型的描述而这是Data Contract需要解决的问题我们知道不同的技术平台对数据类型的表示是不一样的可能某一种技术平台使用bit来表述一个浮点数另一种则使用bit所以要想实现不同技术平台的互操作将不同技术平台同一类型的数据以一种厂商中立的形式来描述是必须的 概括的说SOA中的Service Contract和Data Contract就是一种厂商中立的数据呈现方式对Service Interface和Data Type的而Service的调用都是通过SOAP Message来实现SOAP是基于XML而对于XML结构的定义我们很自然地想到XSD我们可简单地将SOA中的Contract看成是一个XSD Contract in WCF 上面我们实际上是在一个厂商中立的前提下探讨Contract这里的Contract和具体的平台和技术无关接下来我们要谈的是基于技术的话题讨论一下WCF下的Contract简单地说WCF中的Contract主要的功能就是如何将一个基于NET的CLR TypeInterface或者Class转化成一个我们上面提到的Neutral Contract比如如果我们在一个Interface和它的成员上分别运用Service Contract Attribute和Operation Contract当我们Host实现了该Interface的Service的时候WCF就能将在一个NETspecific的CLR Type暴露成一个Neutral Service Contract同理对于一个我们通过在一个Class和它的成员上分别添加DataContractAttribute和DataMemberAttribute就可以就该CLR Type转变成Neutral Data Contract 比如我们一个运用了DataContractAttribute和DataMemberAttribute的Order class namespace ArtechDataContractVersioningService { [DataContract(Namespace=)] public class Order { [DataMember(Order = )] public Guid OrderID {get;set;} [DataMember(Order = )] public DateTime OrderDate { get; set; } [DataMember(Order = )] public Guid SupplierID { get; set; } } } 就可以转变成另一种厂商中立的以XSD表示的Neutral Data Contract
xmlns:xs= xmlns:tns= xmlns:ser=> namespace="" /> [System.Runtime.Serialization.DataContractAttribute(Name="Order", Namespace=" datacontractversioning")] [System.SerializableAttribute()] public partial class Order : object, System.Runtime.Serialization.IExtensibleDataObject, System.ComponentModel.INotifyPropertyChanged { [System.NonSerializedAttribute()] private System.Runtime.Serialization.ExtensionDataObject extensionDataField; [System.Runtime.Serialization.OptionalFieldAttribute()] private System.Guid OrderIDField; [System.Runtime.Serialization.OptionalFieldAttribute()] private System.DateTime OrderDateField; [System.Runtime.Serialization.OptionalFieldAttribute()] private System.Guid SupplierIDField; [global::System.ComponentModel.BrowsableAttribute(false)] public System.Runtime.Serialization.ExtensionDataObject ExtensionData { get { return this.extensionDataField; } set { this.extensionDataField = value; } } [System.Runtime.Serialization.DataMemberAttribute()] public System.Guid OrderID { get { return this.OrderIDField; } set { if ((this.OrderIDField.Equals(value) != true)) { this.OrderIDField = value; this.RaisePropertyChanged("OrderID"); } } } [System.Runtime.Serialization.DataMemberAttribute(Order=1)] public System.DateTime OrderDate { get { return this.OrderDateField; } set { if ((this.OrderDateField.Equals(value) != true)) { this.OrderDateField = value; this.RaisePropertyChanged("OrderDate"); } } } [System.Runtime.Serialization.DataMemberAttribute(Order=2)] public System.Guid SupplierID { get { return this.SupplierIDField; } set { if ((this.SupplierIDField.Equals(value) != true)) { this.SupplierIDField = value; this.RaisePropertyChanged("SupplierID"); } } } public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged; protected void RaisePropertyChanged(string propertyName) { System.ComponentModel.PropertyChangedEventHandler propertyChanged = this. PropertyChanged; if ((propertyChanged != null)) { propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs( propertyName)); } } } 通过上面这样一个在Client自动生成的Order class,你就可以创建Order对象来调用相应的Service了。这种自动生成代码的方式确实很省事,而且当Service端的Data Contract改变的时候,你只需要Update Service Reference就可以重新生成并覆盖现有的代码。但是,就我个人来说,我不要喜欢使用这样的方式,如果对Service暴露出来的数据结构很熟悉的话,我宁愿自己编写这样的class。特别地,对于WCF-WCF(Client和Service都是WCF),如果可能的话,让定义Contract的Assembly在Service和contract共享,我想是最直接的方式。 上面我们说所说的都是根据Service暴露出来的、以厂商中立方式体现的(比如XSD)Client端生成或者自行创建与之相对的Data type。但是对于下面这样的场景,重建Data Type却不是一个好的选择:Client现在已经有一个Order class,而且很多的业务逻辑均依赖于这个class,现在需要调用一个现有的Order Processing Service对Order作某种处理,但是Service 的Order Type,说得更准确地,Service暴露出来的Order Data Contract和Client现有的Order class不太一致,很显然在这种情况下,Client端部可能使用本地Order对象来调用该Service,因为Client提供的数据不符合该Data Contract,如果想上面讲到了重新生成或者创建一个新的Order class,就意味着其他依赖于现有Order class的业务逻辑均会受其影响。所以,在这里,我们需要WCF Data Contract提供给我们的另一种功能——适配功能:通过现有的CLR Type上添加或者改变DataContractAttribute 或者DataMemberAttribute的参数来使现有的CLR Type符合一个既定的Data Contract。究其本质,无论将CLR Type暴露成一个Neutral Contract也好,将CLR Type与既定的Neutral Contract进行适配也罢,这两种功能都是等效的。 接下来,我们就根据一个例子来讨论WCF Data Contract如何将一个现有的CLR Type与一个既定的Neutral Data Contract匹配。 Data Contract Mapping Mechanism 通过上面的介绍,我们发现WCF Data Contract就如同一个适配器,弥合了 CLR Type和Neutral Contract的差异,很容易地实现了他们之间的匹配。接下来,我们就以一个实际的例子来介绍WCF DataContract的这种适配功能:通过DataContractAttribute的修饰,实现了将一个现有Data Type向一个既定的Neutral Data Contract进行适配,从而实现了对基于该Neutral Data Contract的Service 进行正常调用的目的。 我们就以上面提到的Order Class为例,Service端的Order class最终暴露成一个以XSD表示的Neutral Contract: Order class: namespace Artech.DataContractVersioning.Service { [DataContract(Namespace="")] public class Order { [DataMember(Order = 0)] public Guid OrderID {get;set;} [DataMember(Order = 1)] public DateTime OrderDate { get; set; } [DataMember(Order = 2)] public Guid SupplierID { get; set; } } } XSD: xmlns:xs="" xmlns:tns=""xmlns:ser=""> namespace="" /> public class CustomOrder{ [DataMember(Order = 0, Name="OrderID")] public Guid OrderNo { get; set; } [DataMember(Order = 2, Name = "SupplierID")] public Guid SupplierNo { get; set; } [DataMember(Order = 1)] public DateTime OrderDate { get; set; } [DataMember(Order = 3)] public string ShippingAddress { get; set; } } 通过在DataContractAttribute指定Name和Namespace使Data Contract和Namespace和既定的Contract相匹配,通过DataMemberAttribute的Name和Order参数是成员的名称和次序与既定的Contract相匹配。 |