通过第一部分的介绍,我们可以体会到,WCF 的Data Contract在CLR Type和Neutral Contract之间搭建了一座桥梁,弥合了.NET世界和厂商中立世界的差异。通过WCF Data Contract我们将CLR Data Type暴露成一个厂商中立的数据结构的描述,同样通过WCF Data Contract我们将一个现有的CLR Data Type和既定的Neutral contract进行适配。
在.NET中,基于Primary Type,比如Int32,String等等,他们具有一个简单的默认的序列化方式和结构,可以说他们不需要Data Contract。接下来我们主要讨论的是一些相对比较特殊的、完全基于.NET的Data Type,比如Generic、Collection,和Dictionary。首先,我们结合例子来谈谈基于Generic的Data Type的Data Contract。
假设我们需要创建一个用于处理一些单据(Bill)的Service,比如如Order Bill、Sales Bill等。一般的单据都有一个单据头(Header)和明细(Detail)列表,为此我们创建了一个Generic的Bill。并
namespace Artech.SpecialDataContract.Contract { [DataContract] public class Bill < THeader,TDetail > { [DataMember] public THeaderHeader { get ; set ;} [DataMember]public IList < TDetail > DetailList { get ; set ;} } [DataContract]public class OrderHeader { [DataMember] public GuidOrderID { get ; set ;} [DataMember]public DateTimeOrderDate { get ; set ;} } [DataContract]public class OrderDetail { [DataMember] public GuidProductID { get ; set ;} [DataMember]public int Quantity { get ; set ;} } }
为处理订单单据创建了机遇订单的Header和Detail。
对于一个Neutral Service Contract和Neutral Data Contract本身是不可能支持Generic的,也就是Neutral Contract只能是对一个具体的CLR Type的体现。所以在定义Service Contract的时候,对于那些包含Generic Type作为参数或者返回值得Operation,我们必须指定一个具体的Data Type。所以我们创建了如下一个IBillManager Service Contract:
namespace Artech.SpecialDataContract.Contract { [ServiceContract] public interface IBillManager { [OperationContract] void Procss(Bill < OrderHeader,OrderDetail > orderBill); } }
如何我们现在
Host基于这样一个Contract的Service,你猜我们作为参数的数据类型将会如何体现的。
通过WSDL,我们会发现该Service的Data Contract将会以下面一段XSD的方式来呈现:
<? xmlversion="1.0"encoding="utf-8" ?> < xs:schema elementFormDefault ="qualified" targetNamespace ="http://schemas.datacontract.org/2004/07/Artech.SpecialDataContract.Contract" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns ="http://schemas.datacontract.org/2004/07/Artech.SpecialDataContract.Contract" xmlns:ser="http://schemas.microsoft.com/2003/10/Serialization/" > < xs:import schemaLocation ="http://artech/Artech.SpecialDataContract/BillManagerService.svc?xsd=xsd1" namespace="http://schemas.microsoft.com/2003/10/Serialization/" /> < xs:complexType name ="BillOfOrderHeaderOrderDetailLZ9Dq20o" > < xs:annotation > < xs:appinfo > < GenericType Name ="BillOf{0}{1}{#}" Namespace ="http://schemas.datacontract.org/2004/07/Artech.SpecialDataContract.Contract" xmlns="http://schemas.microsoft.com/2003/10/Serialization/" > < GenericParameter Name ="OrderHeader" Namespace ="http://schemas.datacontract.org/2004/07/Artech.SpecialDataContract.Contract" /> < GenericParameter Name ="OrderDetail" Namespace ="http://schemas.datacontract.org/2004/07/Artech.SpecialDataContract.Contract" /> </ GenericType > </ xs:appinfo > </ xs:annotation > < xs:sequence > < xs:element minOccurs ="0" name ="DetailList" nillable ="true" type ="tns:ArrayOfOrderDetail" /> < xs:element minOccurs ="0" name ="Header" nillable ="true" type ="tns:OrderHeader" /> </ xs:sequence > </ xs:complexType > < xs:element name ="BillOfOrderHeaderOrderDetailLZ9Dq20o" nillable ="true" type ="tns:BillOfOrderHeaderOrderDetailLZ9Dq20o" /> < xs:complexType name ="ArrayOfOrderDetail" > < xs:sequence > < xs:element minOccurs ="0" maxOccurs ="unbounded" name ="OrderDetail" nillable ="true" type ="tns:OrderDetail" /> </ xs:sequence > </ xs:complexType > < xs:element name ="ArrayOfOrderDetail" nillable ="true" type ="tns:ArrayOfOrderDetail" /> < xs:complexType name ="OrderDetail" > < xs:sequence > < xs:element minOccurs ="0" name ="ProductID" type ="ser:guid" /> < xs:element minOccurs ="0" name ="Quantity" type ="xs:int" /> </ xs:sequence > </ xs:complexType > < xs:element name ="OrderDetail" nillable ="true" type ="tns:OrderDetail" /> < xs:complexType name ="OrderHeader" > < xs:sequence > < xs:element minOccurs ="0" name ="OrderDate" type ="xs:dateTime" /> < xs:element minOccurs ="0" name ="OrderID" type ="ser:guid" /> </ xs:sequence > </ xs:complexType > < xs:element name ="OrderHeader" nillable ="true" type ="tns:OrderHeader" /> </ xs:schema >
对于不习惯看XSD的朋友,我们可以通过Add Service Reference的方式创建本地的Proxy file,借助生成的与之对应的Class来看看这个XSD最终呈现的结构:
[System.Diagnostics.DebuggerStepThroughAttribute()] [System.CodeDom.Compiler.GeneratedCodeAttribute( " System.Runtime.Serialization " , " 3.0.0.0 " )] [System.Runtime.Serialization.DataContractAttribute(Name = " BillOfOrderHeaderOrderDetailLZ9Dq20o " ,Namespace = " http://schemas.datacontract.org/2004/07/Artech.SpecialDataContract.Contract " )] [System.SerializableAttribute()] public partial class BillOfOrderHeaderOrderDetailLZ9Dq20o: object ,System.Runtime.Serialization.IExtensibleDataObject,System.ComponentModel.INotifyPropertyChanged { [System.NonSerializedAttribute()] private System.Runtime.Serialization.ExtensionDataObjectextensionDataField; [System.Runtime.Serialization.OptionalFieldAttribute()] private Artech.SpecialDataContract.Client.BillManagerService.OrderDetail[]DetailListField; [System.Runtime.Serialization.OptionalFieldAttribute()] private Artech.SpecialDataContract.Client.BillManagerService.OrderHeaderHeaderField; [global::System.ComponentModel.BrowsableAttribute( false )] public System.Runtime.Serialization.ExtensionDataObjectExtensionData { get { return this .extensionDataField; } set { this .extensionDataField = value; } } [System.Runtime.Serialization.DataMemberAttribute()]public Artech.SpecialDataContract.Client.BillManagerService.OrderDetail[]DetailList { get { return this .DetailListField; } set { if (( object .ReferenceEquals( this .DetailListField,value) != true )) { this .DetailListField = value; this .RaisePropertyChanged( " DetailList " ); } } } [System.Runtime.Serialization.DataMemberAttribute()]public Artech.SpecialDataContract.Client.BillManagerService.OrderHeaderHeader { get { return this .HeaderField; } set { if (( object .ReferenceEquals( this .HeaderField,value) != true )) { this .HeaderField = value; this .RaisePropertyChanged( " Header " ); } } }public event System.ComponentModel.PropertyChangedEventHandlerPropertyChanged; protected void RaisePropertyChanged( string propertyName) { System.ComponentModel.PropertyChangedEventHandlerpropertyChanged = this .PropertyChanged; if ((propertyChanged != null )) { propertyChanged( this , new System.ComponentModel.PropertyChangedEventArgs(propertyName)); } } } [System.Diagnostics.DebuggerStepThroughAttribute()] [System.CodeDom.Compiler.GeneratedCodeAttribute(" System.Runtime.Serialization " , " 3.0.0.0 " )] [System.Runtime.Serialization.DataContractAttribute(Name = " OrderHeader " ,Namespace = " http://schemas.datacontract.org/2004/07/Artech.SpecialDataContract.Contract " )] [System.SerializableAttribute()] public partial class OrderHeader: object ,System.Runtime.Serialization.IExtensibleDataObject,System.ComponentModel.INotifyPropertyChanged { [System.NonSerializedAttribute()] private System.Runtime.Serialization.ExtensionDataObjectextensionDataField; [System.Runtime.Serialization.OptionalFieldAttribute()] private System.DateTimeOrderDateField; [System.Runtime.Serialization.OptionalFieldAttribute()] private System.GuidOrderIDField; [global::System.ComponentModel.BrowsableAttribute( false )] public System.Runtime.Serialization.ExtensionDataObjectExtensionData { get { return this .extensionDataField; } set { this .extensionDataField = value; } } [System.Runtime.Serialization.DataMemberAttribute()]public System.DateTimeOrderDate { get