[原创]谈谈WCF中的Data Contract (1):Data Contract Overview
[2] [原创]谈谈WCF中的Data Contract (1):Data Contract Overview
[3] [原创]谈谈WCF中的Data Contract (1):Data Contract Overview
上面我们实际上是在一个厂商中立的前提下探讨Contract,这里的Contract和具体的平台和技术无关。接下来我们要谈的是基于技术的话题:讨论一下WCF下的Contract。简单地说,WCF中的Contract主要的功能就是如何将一个基于.NET的CLR Type,Interface或者Class,转化成一个我们上面提到的Neutral Contract。比如,如果我们在一个Interface和它的成员上分别运用Service Contract Attribute和Operation Contract,当我们Host实现了该Interface的Service的时候,WCF就能将在一个.NET-specific的CLR Type暴露成一个Neutral Service Contract。同理对于一个,我们通过在一个Class和它的成员上分别添加DataContractAttribute和DataMemberAttribute,就可以就该CLR Type转变成Neutral Data Contract。
比如我们一个运用了DataContractAttribute和DataMemberAttribute的Order class:
{
[DataContract(Namespace="http://artech.datacontractversioning")]
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表示的Neutral Data Contract:
<xs:schema elementFormDefault="qualified" targetNamespace="http://artech.datacontractversioning"
xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://artech.datacontractversioning"
xmlns:ser="http://schemas.microsoft.com/2003/10/Serialization/">
<xs:import schemaLocation="http://artech/Artech.DataContractVersioning/OrderManagerService.svc?xsd=xsd1"
namespace="http://schemas.microsoft.com/2003/10/Serialization/" />
<xs:complexType name="Order">
<xs:sequence>
<xs:element minOccurs="0" name="OrderID" type="ser:guid" />
<xs:element minOccurs="0" name="OrderDate" type="xs:dateTime" />
<xs:element minOccurs="0" name="SupplierID" type="ser:guid" />
</xs:sequence>
</xs:complexType><xs:element name="Order" nillable="true" type="tns:Order"/></xs:schema>
当Client需要调用该Order type的Service的时候,在本地需要一个Data Type能够匹配上面的以XSD体现的Data Contract。一般地,我们可以在VS中通过Add Service Reference的方式或者通过一些Tools,比如XSDUtil和SvcUtil来生成这样的Class。比如我们通过Add Service Reference方式,就可以生成下面一个对应的Order class:
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "3.0.0.0")]
[System.Runtime.Serialization.DataContractAttribute(Name="Order", Namespace="http://
artech.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匹配。