程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> 談談WCF中的Data Contract (1):Data Contract Overview

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

編輯:關於.NET

Contract in SO:Contract是對操作和數據的抽象

在我們看來,Service Orientation提供了一種對業務、功能進行分解的方式。針對SO,我們把一個具體的業務流程或者一個復雜的功能分解成一個個獨立完成某項任務的子單元,這些子單元通過一個個Service來承載。對於Service本身來講,他們應該是自治的,獨自完成自己的功能、不依賴於其他的Service。但是Service的價值體現在它被潛在的消費者使用的程度。這實際上包含兩方面的內容,作為Service本身,它如何將自己暴露出來,供一切可能的潛在用戶調用,這些潛在用戶不僅僅指那些不同的Client,也包含其他的Service:Service 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調用攜帶的數據(參數和返回值),更用於表示這個調用本身,以及滿足各種要求的控制信息, 比如基於Security、Session、Reliable Messaging、Transaction等等的控制信息。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,比如我們通常使用Request-Response的方式來向Service發送Request進而得到返回結果,我們也可以以Request-Forget的形式來異步地調用Service(不需要從Service獲取Response),我們可以讓一個Service在沒有收到任何Request的情況下,以廣播的形式向注冊的Client發送通知,當然我們還有其他不同的消息交互的模式,我們把這些不同的信息交互方式稱為MEP(Message Exchange Pattern)。也就是說,一個Operation最終通過被最終轉換成了按照某種MEP進行的消息交互,而Service Contract旨在實現對這種MEP的描述,比如是否需要Request Message或者Response Message(如果僅僅有Response Message就是Notification的方式;如果僅僅具有Request Message,那就是我們上面談到的Request-Forget的模式),和Message本身具有的格式。

上面我們說了Service Contract是以一種廠商中立的形式描述體現為某種模式的消極交互、構成整個Service的所有Operation。而我們也說了Consumer和Service的交互本質上看就是按照某種Pattern體現的一次Message Exchange,好像具有了Service Contract的描述就可以了。但是實際上,單單有了Service Contract對Service的描述還不夠,因為Service Contract本身缺乏對攜帶於Message,用於信息傳遞的數據類型的描述,而這是Data Contract需要解決的問題。我們知道不同的技術平台對數據類型的表示是不一樣的,可能某一種技術平台使用16bit來表述一個浮點數,另一種則使用32bit。所以要想實現不同技術平台的互操作,將不同技術平台同一類型的數據以一種廠商中立的形式來描述是必須的。

概括的說,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 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:

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:

<?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 type的Service的時候,在本地需要一個Data Type能夠匹配上面的以XSD體現的Data Contract。一般地,我們可以在VS中通過Add Service Reference的方式或者通過一些Tools,比如XSDUtil和SvcUtil來生成這樣的Class。比如我們通過Add Service Reference方式,就可以生成下面一個對應的Order class:

[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-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="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; }
}

仔細分析CustomOrder和Service的Order以及XSD,我們發現兩者除了結構一樣之外,沒有一處使相同的,具體體現在:     

Class Name不一樣。

NameSpace不一樣,Order和XSD的namespace為http://artech.datacontractversioning,而CustomOrder卻沒有顯示指定一個Namespace(這樣WCF會為其指定一個默認的namespace)。

成員的名稱不一樣。

成員的順利不一樣。

如果我們現在要使我們的CustomOrder滿足現有的Order Data Contract,我們就需要消除這些不同之處,通過DataContractAttribute和DataMemberAttribute,這樣的問題根本就不是問題,下面就是我們重新定義的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指定Name和Namespace使Data Contract和Namespace和既定的Contract相匹配,通過DataMemberAttribute的Name和Order參數是成員的名稱和次序與既定的Contract相匹配。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved