談談WCF中的Data Contract (1):Data Contract Overview

 Contract in SOContract是對操做和數據的抽象

在咱們看來,Service Orientation提供了一種對業務、功能進行分解的方式。針對SO,咱們把一個具體的業務流程或者一個複雜的功能分解成一個個獨立完成某項任務的子單元,這些子單元經過一個個Service來承載。對於Service自己來說,他們應該是自治的,獨自完成本身的功能、不依賴於其餘的Service。可是Service的價值體如今它被潛在的消費者使用的程度。這實際上包含兩方面的內容,做爲Service自己,它如何將本身暴露出來,供一切可能的潛在用戶調用,這些潛在用戶不只僅指那些不一樣的Client,也包含其餘的ServiceService Orientation其中一個特徵就是「Service should be composite,鼓勵將一個個相關細粒度的Service組合成一個大的Service。這樣有利於較大限度的實現重用,而重用每每意味着更小的投入、更佳的可維護性。而另外一方面就是這些消費者經過怎樣的方式來調用它所須要的Service html

這實際上體現了二者相互交互的問題。在一個分佈式的環境中要實現二者的交互,有兩個必需要解決的問題:如何保證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全部OperationInterface(而不是Implementation)。說得更加具體點,你們都知道ConsumerService之間的交互都是經過Message的形式來實現的,一次交互就是一次Message Exchange。在不一樣的場景,咱們以不經過Pattern來進程Message Exchange,好比咱們一般使用Request-Response的方式來向Service發送Request進而獲得返回結果,咱們也能夠以Request-Forget的形式來異步地調用Service(不須要從Service獲取Response),咱們可讓一個Service在沒有收到任何Request的狀況下,以廣播的形式向註冊的Client發送通知,固然咱們還有其餘不一樣的消息交互的模式,咱們把這些不一樣的信息交互方式稱爲MEPMessage Exchange Pattern)。也就是說,一個Operation最終經過被最終轉換成了按照某種MEP進行的消息交互,而Service Contract旨在實現對這種MEP的描述,好比是否須要Request Message或者Response Message(若是僅僅有Response Message就是Notification的方式;若是僅僅具備Request Message,那就是咱們上面談到的Request-Forget的模式),和Message自己具備的格式。 app

上面咱們說了Service Contract是以一種廠商中立的形式描述體現爲某種模式的消極交互、構成整個Service的全部Operation。而咱們也說了ConsumerService的交互本質上看就是按照某種Pattern體現的一次Message Exchange,好像具備了Service Contract的描述就能夠了。可是實際上,單單有了Service ContractService的描述還不夠,由於Service Contract自己缺少對攜帶於Message,用於信息傳遞的數據類型的描述,而這是Data Contract須要解決的問題。咱們知道不一樣的技術平臺對數據類型的表示是不同的,可能某一種技術平臺使用16bit來表述一個浮點數,另外一種則使用32bit。因此要想實現不一樣技術平臺的互操做,將不一樣技術平臺同一類型的數據以一種廠商中立的形式來描述是必須的。 異步

歸納的說,SOA中的Service ContractData Contract就是一種廠商中立的數據呈現方式對Service InterfaceData Type的。而Service的調用都是經過SOAP Message來實現,SOAP是基於XML,而對於XML結構的定義,咱們很天然地想到XSD,咱們可簡單地將SOA中的Contract當作是一個XSD

Contract in WCF 分佈式

上面咱們其實是在一個廠商中立的前提下探討Contract,這裏的Contract和具體的平臺和技術無關。接下來咱們要談的是基於技術的話題:討論一下WCF下的Contract。簡單地說,WCF中的Contract主要的功能就是如何將一個基於.NETCLR TypeInterface或者Class,轉化成一個咱們上面提到的Neutral Contract。好比,若是咱們在一個Interface和它的成員上分別運用Service Contract AttributeOperation Contract,當咱們Host實現了該InterfaceService的時候,WCF就能將在一個.NET-specificCLR Type暴露成一個Neutral Service Contract。同理對於一個,咱們經過在一個Class和它的成員上分別添加DataContractAttributeDataMemberAttribute,就能夠就該CLR Type轉變成Neutral Data Contract ide

好比咱們一個運用了DataContractAttributeDataMemberAttributeOrder class ui

namespace Artech.DataContractVersioning.Service
{
    [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
this

<? xml version="1.0" encoding="utf-8" ?>
< 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 typeService的時候,在本地須要一個Data Type可以匹配上面的以XSD體現的Data Contract。通常地,咱們能夠在VS中經過Add Service Reference的方式或者經過一些Tools,好比XSDUtilSvcUtil來生成這樣的Class。好比咱們經過Add Service Reference方式,就能夠生成下面一個對應的Order class: spa

[System.Diagnostics.DebuggerStepThroughAttribute()]
    [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-WCFClientService都是WCF),若是可能的話,讓定義ContractAssemblyServicecontract共享,我想是最直接的方式。

上面咱們說所說的都是根據Service暴露出來的、以廠商中立方式體現的(好比XSDClient端生成或者自行建立與之相對的Data type。可是對於下面這樣的場景,重建Data Type卻不是一個好的選擇:Client如今已經有一個Order class,並且不少的業務邏輯均依賴於這個class,如今須要調用一個現有的Order Processing ServiceOrder做某種處理,可是Service Order Type,說得更準確地,Service暴露出來的Order Data ContractClient現有的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 TypeNeutral Contract的差別,很容易地實現了他們之間的匹配。接下來,咱們就以一個實際的例子來介紹WCF DataContract的這種適配功能:經過DataContractAttribute的修飾,實現了將一個現有Data Type向一個既定的Neutral Data Contract進行適配,從而實現了對基於該Neutral Data ContractService 進行正常調用的目的。

咱們就以上面提到的Order Class爲例,Service端的Order class最終暴露成一個以XSD表示的Neutral Contract

Order class

namespace Artech.DataContractVersioning.Service
{
    [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

<? xml version="1.0" encoding="utf-8" ?>
< 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有一個與之結構類似的CustomOrder class

public class CustomOrder
   
{
       
public Guid OrderNo
       
{ get; set; }

       
public Guid SupplierNo
       
{ get; set; }

       
public DateTime OrderDate
       
{ get; set; }
}

仔細分析CustomOrderServiceOrder以及XSD,咱們發現二者除告終構同樣以外,沒有一處使相同的,具體體如今:         

  • Class Name 不同。
  • NameSpace 不同, Order XSD namespace http://artech.datacontractversioning ,而 CustomOrder 卻沒有顯示指定一個 Namespace (這樣 WCF 會爲其指定一個默認的 namespace )。
  • 成員的名稱不同。
  • 成員的順利不同。

若是咱們如今要使咱們的CustomOrder知足現有的Order Data Contract,咱們就須要消除這些不一樣之處,經過DataContractAttributeDataMemberAttribute,這樣的問題根本就不是問題,下面就是咱們從新定義的CustomOrder class

  [DataContract(Name = " Order " ,Namespace = " http://artech.datacontractversioning " )]
   
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指定NameNamespace使Data ContractNamespace和既定的Contract相匹配,經過DataMemberAttributeNameOrder參數是成員的名稱和次序與既定的Contract相匹配。

[原創]談談WCF中的Data Contract(1):Data Contract Overview
[原創]談談WCF中的Data Contract(2):WCF Data Contract對Generic的支持
[原創]談談WCF中的Data Contract(3):WCF Data Contract對Collection & Dictionary的支持
[原創]談談WCF中的Data Contract(4):WCF Data Contract Versioning

相關文章
相關標籤/搜索