共用方式為


開發人員 Windows Communication Foundation 4 簡介

Aaron Skonnard、Pluralsight

原始:2009 年 11 月

更新為 RTM:2010 年 4 月

概觀

.NET 4 隨附一些吸引人的新功能,並歡迎改善 Windows Communication Foundation (WCF) 領域。 這些 WCF 增強功能主要著重于簡化開發人員體驗、啟用更多通訊案例,以及透過讓「工作流程服務」成為第一級公民,與 Windows Workflow Foundation (WF) 提供豐富的整合。

好消息是大部分的 WCF 4 變更著重于讓現今的常見案例更容易,並讓新的通訊案例和開發樣式變得可行。 因此,將現有的 WCF 解決方案移至 .NET 4,在移轉方面會相當順暢。 然後,您只需決定您想要在解決方案中利用哪些 WCF 4 功能。 本文的其餘部分將為您介紹每個新的 WCF 4 功能區域,並示範其運作方式。

WCF 4 的新功能

WCF 4 隨附各種不同的特定功能,但圖 1 說明我們將在整個檔中著重的主要功能區域。 這些功能區域摘要說明 WCF 4 的新功能,並醒目提示此版 .NET Framework 所提供的最上層機會。

圖 1:WCF 4 功能區域

功能範圍 Description

簡化的組態

透過支援預設端點、系結和行為組態,簡化 WCF 組態區段。 這些變更可讓您裝載無組態服務,大幅簡化最常見 WCF 案例的開發人員體驗。

探索

特定和受控服務探索行為的新架構支援,其符合標準WS-Discovery通訊協定。

路由服務

您可以在 WCF 解決方案中使用的可設定路由服務的新架構支援。 提供內容型路由、通訊協定橋接和錯誤處理的功能。

REST 改善

WCF WebHttp 服務的增強功能與工具,可簡化 REST 服務開發。

工作流程服務

將 WCF 與 WF 整合的豐富架構支援,以實作宣告式長時間執行的工作流程服務。 這個新的程式設計模型提供您兩個架構都必須提供 (WCF & WF) 的最佳架構。

一旦完成涵蓋這些主要功能領域之後,我們將簡短討論 .NET 4 隨附的一些更進階較低層級 WCF 功能,包括改善的類型解析功能、支援具有競爭取用者 (「接收內容」的佇列、) 、透過位元組串流編碼器支援未包裝的二進位資料,以及支援高效能 ETW 型追蹤。

在我們完成時,您會看到 WCF 4 更容易使用,並為一些現今最常見的案例和開發樣式提供更內建的支援。

簡化的組態

WCF 在 3.x 中提供的統一程式設計模型同時是一種簡單和大寫,它可簡化各種不同通訊案例的服務邏輯撰寫服務邏輯,不過它也會增加設定端的複雜度,因為它提供許多您強制瞭解的不同基礎通訊選項,才能開始。

事實上,WCF 組態通常是在實務上使用 WCF 的最昂貴區域,而大部分的複雜度都落在未準備處理它的 IT/作業人員上。

假設此實境,當您考慮使用 WCF 3.x 的淨複雜度時,可能會合理地認為比其前置 ASP.NET Web 服務 (ASMX) 更難使用。 使用 ASMX,您可以定義 [WebMethod] 作業,而且執行時間會自動提供基礎通訊的預設組態。 另一方面,移至 WCF 3.x 時,開發人員必須充分瞭解各種 WCF 組態選項,才能定義至少一個端點。 而設定選項數目令人感到困難,通常讓某些開發人員感到困難。

為了讓整體 WCF 體驗與 ASMX 一樣簡單,WCF 4 隨附新的「預設設定」模型,可完全移除任何 WCF 組態的需求。 如果您未針對特定服務提供任何 WCF 組態,WCF 4 執行時間會自動使用一些標準端點和預設系結/行為組態來設定您的服務。 這可讓您更輕鬆地啟動並執行 WCF 服務,特別是對於不熟悉各種 WCF 組態選項且至少接受預設值的使用者。

預設端點

使用 WCF 3.x 時,如果您嘗試裝載不含任何已設定端點的服務,ServiceHost 實例將會擲回例外狀況,通知您至少需要設定一個端點。 使用 WCF 4 時,這已不再是這種情況,因為執行時間會自動為您新增一或多個「預設端點」,因此不需要任何設定即可使用服務。

其運作方式如下。 當主應用程式在 ServiceHost 實例上呼叫 Open 時,它會從應用程式組態檔建置內部服務描述,以及主應用程式可能已明確設定的任何專案,如果已設定的端點數目仍然為零,則會呼叫 AddDefaultEndpoints,這是 ServiceHost 類別上找到的新公用方法。 此方法會根據 IIS 案例中的服務基底位址 (,將一或多個端點新增至服務描述,這是 .svc 位址) 。 由於 方法是公用的,因此您也可以直接在自訂裝載案例中呼叫它。

為了精確,AddDefaultEndpoints 的實作會針對服務所實作的每個服務合約,為每個基底位址新增一個預設端點。 例如,如果服務實作兩個服務合約,並使用單一基底位址來設定主機,AddDefaultEndpoints 會為每個服務合約設定一個預設端點 (一個服務合約) 。 不過,如果服務實作兩個服務合約,且主機設定兩個基底位址 (一個用於 HTTP,另一個用於 TCP) ,AddDefaultEndpoints 就會使用四個預設端點來設定服務。

讓我們看看一個完整的範例來說明運作方式。 假設您有下列 WCF 服務合約和下列服務實作:

[ServiceContract]

公用介面 IHello

{

    [OperationContract]

    void SayHello (字串名稱) ;

}

[ServiceContract]

公用介面 IGoodbye

{

    [OperationContract]

    void SayGoodbye (字串名稱) ;

}

public 類別 GreetingService :IHello,IGoodbye // 服務會實作這兩個合約

{

    public void SayHello (字串名稱)

    {

        Console.WriteLine (「Hello {0} 」, name) ;

    }

    public void SayGoodbye (字串名稱)

    {

        Console.WriteLine (「Goodbye {0} 」, name) ;

    }

}

使用 WCF 4,您現在可以使用 ServiceHost 來裝載 GreetingService 服務,而不需要任何應用程式設定。 在自訂裝載案例中使用 ServiceHost 時,您必須指定要使用的一或多個基底位址。 以下顯示如何在主控台應用程式中裝載 GreetingService,然後再次假設沒有與此程式相關聯的app.config檔案:

類別 Program

{

    static void Main(string[] args)

    {

        主機設定了兩個基底位址,一個用於 HTTP,另一個用於 TCP

        ServiceHost 主機 = new ServiceHost (typeof (GreetingService) ,

            new Uri (「 https://localhost:8080/greeting" ;) ,

            new Uri (「net.tcp://localhost:8081/greeting」) ) ;

        主機。Open () ;

        foreach (host 中的 ServiceEndpoint se。Description.Endpoints)

            Console.WriteLine (「A: , B: {0} , C: {1}{2} 」,

                硒。位址、se.Binding.Name se.Contract.Name) ;

        Console.WriteLine (「按 < Enter > 鍵停止服務」。) ;

        Console.ReadLine();

        主機。Close () ;

    }

}

此範例會使用兩個基底位址來設定 ServiceHost:一個用於 HTTP,另一個用於 TCP。 當您執行此程式時,您會看到列印到主控台視窗的四個端點,如圖 2 所示。 您會針對 HTTP 基底位址取得兩個,每個合約一個,而兩個用於 TCP 基底位址,另一個用於每個合約。 這全都是由 ServiceHost 實例在幕後提供。

圖 2:主控台視窗中顯示的預設端點

請注意 WCF 如何選擇針對預設 HTTP 端點使用 BasicHttpBinding,以及預設 TCP 端點的 NetTcpBinding。 我很快就會示範如何變更這些預設值。

請記住,此預設端點行為只會在服務未設定任何端點時啟動。 如果我變更控制台應用程式以至少一個端點來設定服務,您就不會再看到這些預設端點顯示在輸出中。 為了說明這一點,我只會新增下列程式碼,以在建構 ServiceHost 實例之後呼叫 AddServiceEndpoint:

...

ServiceHost 主機 = new ServiceHost (typeof (GreetingService) ,

    new Uri (「 https://localhost:8080/greeting" ;) ,

    new Uri (「net.tcp://localhost:8081/greeting」) ) ;

主機。AddServiceEndpoint (typeof (IHello) , new WSHttpBinding () , 「myendpoint」) ;

...

如果您使用插入這一行程式碼來執行主控台應用程式,您會發現只有單一端點現在會出現在輸出中–我們在上述程式碼中手動設定的端點 (請參閱圖 3) 。

圖 3:使用單一端點設定主機之後的主控台輸出

不過,如果您想要新增預設端點集以及您自己的端點,您一律可以自行呼叫 AddDefaultEndpoints。 下列程式碼範例說明如何執行此動作:

...

ServiceHost 主機 = 新的 ServiceHost (typeof (GreetingService) ,

    新 Uri (「 https://localhost:8080/greeting" ;) ,

    新的 Uri (「net.tcp://localhost:8081/greeting」) ) ;

主機。AddServiceEndpoint (typeof (IHello) , new WSHttpBinding () , 「myendpoint」) ;

主機。AddDefaultEndpoints () ;

...

如果您再次使用此變更執行主控台應用程式,您會看到主控台視窗中顯示五個端點–我手動設定的端點以及四個預設端點 (請參閱圖 4) 。

圖 4:手動呼叫 AddDefaultEndpoints 之後的主控台輸出

既然我們已瞭解在執行時間將預設端點新增至服務的演算法和機制,下一個問題是 WCF 如何決定要用於特定型位址的系結?

預設通訊協定對應

這個問題的答案很簡單。 WCF 定義傳輸通訊協定配置之間的預設通訊協定對應 (例如 HTTP、net.tcp、net.pipe 等) 和內建 WCF 系結。 在 .NET 4 machine.config.comments 檔案中找到預設通訊協定對應,如下所示:

<system.serviceModel>

   <protocolMapping>

      <add scheme=「HTTP」 binding=「basicHttpBinding」 bindingConfiguration=「」 />

      <add scheme=「net.tcp」 binding=「netTcpBinding」 bindingConfiguration=「」/>

      <add scheme=「net.pipe」 binding=「netNamedPipeBinding」 bindingConfiguration=「」/>

      <add scheme=「net.msmq」 binding=「netMsmqBinding」 bindingConfiguration=「」/>

   </protocolMapping>

   ...

您可以將本節新增至machine.config並修改每個通訊協定配置的對應,以覆寫機器層級的這些對應。 或者,如果您只想在應用程式範圍內覆寫它,您可以在應用程式/Web 組態檔內覆寫此區段。

例如,如果您的組織主要著重于使用 WCF 建置 RESTful 服務,將 「HTTP」 通訊協定配置的預設系結變更為 WebHttpBinding 可能很合理。 下列範例說明如何在應用程式組態檔內完成此作業:

<configuration>

  <system.serviceModel>

    <protocolMapping>

      <add scheme=「HTTP」 binding=「webHttpBinding」/>

    </protocolMapping>

  </system.serviceModel>

</配置>

現在,如果我已使用這個app.config重新執行稍早顯示的主控台應用程式,則兩個預設 HTTP 型端點現在會顯示它們正在使用 WebHttpBinding (請參閱圖 5) 。

圖 5:覆寫預設 HTTP 通訊協定對應之後的主控台輸出

一旦 WCF 決定要透過通訊協定對應資料表使用的系結,它會在設定預設端點時使用預設系結組態。 如果您不滿意內建系結預設值,您也可以覆寫特定系結的預設組態。

預設系結組態

除非由特定端點的主應用程式明確覆寫,否則每個 WCF 系結都會隨附預設組態。 除非您選擇套用明確的系結組態來覆寫,否則您使用的每個系結實例一律隨附內建預設值。

在 WCF 3.x 中,您可以定義可透過 bindingConfiguration 屬性套用至端點定義的具名系結組態來執行此動作。 正確執行此動作的機制很麻煩且容易出錯。  下列組態檔顯示典型的範例:

<configuration>

  <system.serviceModel>

    <bindings>

      <basicHttpBinding>

        <系結名稱=「BasicWithMtom」 messageEncoding=「Mtom」/>

      </basicHttpBinding>

    </綁定>

    <services>

      <service name=「GreetingService」>

        <endpoint address=「mtom」 binding=「basicHttpBinding」

                  bindingConfiguration=「BasicWithMtom」

                  contract=「IHello」/>

      </service>

    </服務>

  </system.serviceModel>

</配置>

在上述範例中,「BasicWithMtom」 系結組態會藉由將訊息編碼變更為 MTOM 來覆寫 BasicHttpBinding 的預設值。 不過,當您透過 「bindingConfiguration」 屬性將它套用至特定端點時,此系結組態才會生效 – 這是通常省略開發人員和作業人員的步驟,因而造成設定問題。

使用 WCF 4,您現在只要在定義新組態時省略系結組態名稱,即可定義預設系結組態。 然後 WCF 會針對使用該系結未設定明確系結組態的任何端點使用該預設組態。

例如,如果我們將下列app.config檔案新增至稍早顯示的主控台應用程式,則兩個預設 HTTP 端點會挑選這個預設 BasicHttpBinding 組態,以啟用 MTOM:

<configuration>

  <system.serviceModel>

    <bindings>

      <basicHttpBinding>

        <系結 messageEncoding=「Mtom」/ >< --請注意沒有名稱屬性 -->

      </basicHttpBinding>

    </綁定>

  </system.serviceModel>

</配置>

當然,如果您想要讓這些預設系結組態在機器上執行的所有服務生效,也可以藉由在應用程式佈建檔中新增預設系結組態,依應用程式定義這些預設系結組態,以machine.config。

這項功能提供簡單的機制,讓您定義一組標準系結預設值,您可以在所有服務之間使用,而不需要對其他開發人員或 IT/作業人員產生系結組態的複雜性。 他們只要選擇適當的系結,並放心裝載環境會提供適當的預設設定。

除了預設系結組態之外,您服務和端點要考慮的另一件事就是其預設行為組態應該是什麼。

預設行為組態

WCF 4 也可讓您定義服務和端點的預設行為組態,當您想要在電腦或解決方案內執行的所有服務或端點之間共用標準預設行為設定時,可以簡化事項。

在 WCF 3.x 中,您必須定義您透過 「behaviorConfiguration」 屬性明確套用至服務和端點的具名行為組態。 使用 WCF 4,您可以省略組態定義中的名稱來定義預設行為組態。 如果您將這些預設行為新增至 machine.config,這些行為會套用至電腦上裝載的所有服務或端點。 如果您將它們新增至app.config,它們只會在主應用程式的範圍內生效。 範例如下:

<configuration>

  <system.serviceModel>

    <behaviors>

      <serviceBehaviors>

        <behavior >< --注意到沒有名稱屬性 -->

          <serviceMetadata HTTPGetEnabled=「true」/>

        </行為>

      </serviceBehaviors>

    </行為>

  </system.serviceModel>

</配置>

此範例會開啟任何未具有明確行為設定之服務的服務中繼資料。 如果我們將此預設行為組態新增至稍早顯示的主控台應用程式app.config檔案,然後再次執行應用程式,我們可以流覽至基底 HTTP 位址以擷取服務說明頁面和服務 WSDL 定義 (請參閱圖 6) 。

圖 6:流覽至預設行為設定所啟用的服務中繼資料

WCF 4 的另一項新功能是行為設定現在支援繼承模型。 如果應用程式使用與已在machine.config中定義的相同名稱來定義行為組態,應用程式特定行為組態將會與整個機器組態合併,並將任何其他行為新增至衍生的複合行為組態。

標準端點

與預設端點相關的是另一個稱為「標準端點」的新 WCF 4 功能。 您可以將標準端點視為 WCF 4 架構中內建的常見預先設定端點定義,您可以直接使用。 標準端點會定義您通常不會變更的「標準」端點組態,但您可以稍後看到 。

圖 7 描述隨附于 WCF 4 的標準端點。 這些提供一些最常見的 WCF 4 功能和通訊案例的標準端點定義。 例如,在 MEX 端點的情況下,您一律需要為服務合約指定 IMetadataExchange,而且最有可能選擇 HTTP。 因此,WCF 不會強制您一律手動執行該動作,而是為稱為 「mexEndpoint」 且容易使用的 metdata 交換提供標準端點定義。

圖 7:WCF 4 中的標準端點

標準端點名稱 Description

mexEndpoint

定義針對服務合約使用 IMetadataExchange 設定之 MEX 的標準端點、mexHttpBinding 做為預設系結, (您可以變更此) 和空位址。

dynamicEndpoint

定義設定為在 WCF 用戶端應用程式中使用 WCF 探索的標準端點。 使用此標準端點時,不需要位址,因為在第一次呼叫期間,用戶端會查詢符合指定合約的服務端點,並為您自動連線。 根據預設,探索查詢會透過多播 UDP 傳送,但您可以指定要在需要時使用的探索系結和搜尋準則。

discoveryEndpoint

定義針對用戶端應用程式內探索作業預先設定的標準端點。 使用者在使用這個標準端點時,必須指定位址和系結。

udpDiscoveryEndpoint

定義在多播位址上使用 UDP 系結,針對用戶端應用程式內的探索作業預先設定的標準端點。 衍生自 DiscoveryEndpoint。

announcementEndpoint

定義針對探索公告功能預先設定的標準端點。 使用者在使用這個標準端點時,必須指定位址和系結。

udpAnnouncementEndpoint

定義在多播位址上針對 UDP 系結的公告功能預先設定的標準端點。 此端點衍生自 announcementEndpoint。

workflowControlEndpoint

定義一個標準端點,用於控制工作流程執行個體 (建立、執行、暫停、終止等) 的執行。

webHttpEndpoint

定義以 WebHttpBinding 和 WebHttpBehavior 設定的標準端點。 使用 公開 REST 服務。

webScriptEndpoint

定義使用 WebHttpBinding 和 WebScriptEnablingBehavior 設定的標準端點。 用來公開 Ajax 服務。

只要依名稱參考這些端點,即可利用您自己服務組態中的任何標準端點。 <端點 > 元素現在隨附「種類」屬性,可用來指定標準端點的名稱。 例如,下列範例會利用標準 「mexEndpoint」 定義來設定 GreetingService 與 MEX 端點:

<configuration>

  <system.serviceModel>

    <services>

      <service name=「GreetingService」>

        <endpoint kind=「basicHttpBinding」 contract=「IHello」/>

        <endpoint kind=「mexEndpoint」 address=「mex」 />

      </service>

    </服務>

  </system.serviceModel>

</配置>

雖然標準端點會防護您不受大部分設定詳細資料 (,例如,使用 mexEndpoint 我不需要指定系結或合約) ,但有時候您可能想要使用這些端點,但需要以稍微不同的方式設定標準端點定義。 

當您需要這樣做時,您可以使用 < standardEndpoints > 區段,並覆寫標準端點的端點組態。 然後,您可以透過 endpointConfiguration 屬性定義新的 < 端點 > 時參考該組態,如下所示:

<configuration>

  <system.serviceModel>

    <services>

      <service name=「GreetingService」>

        <endpoint binding=「basicHttpBinding」 contract=「IHello」/>

        <endpoint kind=「udpDiscoveryEndpoint」 endpointConfiguration=「D11」/>

      </service>

    </服務>

    <standardEndpoints>

      <udpDiscoveryEndpoint>

        <standardEndpoint name=「D11」 discoveryVersion=「WSDiscovery11」/>

      </udpDiscoveryEndpoint>

    </standardEndpoints>

    <behaviors>

      <serviceBehaviors>

        <behavior>

          <serviceDiscovery/>

          <serviceMetadata HTTPGetEnabled=「true」/>

        </行為>

      </serviceBehaviors>

    </行為>

  </system.serviceModel>

</配置>

此範例會變更名為 「udpDiscoveryEndpoint」 之標準端點的預設WS-Discovery版本, (我們很快就會討論服務探索) 。

簡化 IIS/ASP.NET 裝載

假設這些預設端點、預設系結組態和預設行為設定的新功能,在 WCF 4 中裝載 IIS/ASP.NET 會變得更容易。 ASP.NET 使用 ASMX 服務的開發人員現在可以定義本質上一樣簡單的 WCF 服務。

事實上,請查看下列 WCF 服務定義有多簡單:

<-- HelloWorld.svc -->

<%@ ServiceHost Language=「C#」 Debug=「true」 Service=「HelloWorldService

    CodeBehind=「~/App_Code/HelloWorldService.cs」 %>

[ServiceContract]

public 類別 HelloWorldService

{

    [OperationContract]

    public string HelloWorld ()

    {

        傳回 「hello, world」;

    }

}

這是最簡單的 WCF 服務定義形式,因為我們不使用個別的介面定義來定義服務合約,而且所有專案都定義在一個檔案 HelloWorld.svc (附注:我不建議使用此方法,只是注意到可以繪製與 ASMX) 的比較。 這應該很像一般 ASMX 服務,主要差異在於您在服務類別上使用的屬性名稱 (例如 [WebService] 和 [WebMethod]) 。 移動元件絕對較少。

使用上一節所述的新 WCF 4 功能,現在您可以流覽至 HelloWorld.svc,而不需要任何其他 WCF 設定,WCF 啟用邏輯會在幕後建立 ServiceHost 實例,並使用單一預設 HTTP 端點進行設定。 如果您已將預設服務行為新增至啟用服務中繼資料的machine.config檔案,當您流覽至 HelloWorld.svc (時,您會看到 WCF 說明頁面和 WSDL 定義的連結,請參閱圖 8) 。

圖 8:HelloWorldService 說明頁面

如果您尚未啟用整個服務中繼資料機器,您可以將下列預設行為組態新增至您的 web.config 檔案,在 Web 應用程式中加以啟用:

...

<system.serviceModel>

  <behaviors>

    <serviceBehaviors>

      <行為 >< --請注意沒有名稱屬性 -->

        <serviceMetadata HTTPGetEnabled=「true」/>

      </行為>

    </serviceBehaviors>

  </行為>

</system.serviceModel>

...

您也可以遵循上一節所述的程式來變更其他預設設定。 例如,您可以變更預設通訊協定對應、新增預設系結組態或其他預設行為組態。 如果服務實作多個服務合約,產生的 ServiceHost 實例將會在每個合約中設定一個 HTTP 端點。

例如,假設我們透過此處顯示的 .svc 檔案,裝載來自先前) 的 GreetingService (:

<-- GreetingService.svc -->

<%@ServiceHost Service=「GreetingService」%>

根據 GreetingService 的定義,您第一次流覽至 GreetingService.svc 時,WCF 啟用邏輯會建立 ServiceHost 實例,而且它會針對 GreetingService 類型新增兩個預設 HTTP 端點, (每個服務合約) 一個。 您可以流覽至 WSDL 定義來確認這一點,而且您會在服務 > 元素內找到兩 < 個 < 埠 > 元素。

整體來說,這些 WCF 設定簡化應該讓 ASP.NET 開發人員更輕鬆地在其 Web 應用程式中啟動並執行 WCF 服務,並讓最簡單的案例更接近開發人員搭配使用 ASP.NET Web 服務的體驗。

無檔案啟用

雖然 .svc 檔案可讓您輕鬆地公開 WCF 服務,但更簡單的方法是在Web.config內定義虛擬啟用端點,藉此完全移除 .svc 檔案的需求。

在 WCF 4 中,您可以定義虛擬服務啟用端點,以對應至Web.config中的服務類型。這可讓您啟用 WCF 服務,而不需維護實體 .svc 檔案 (a.k.a.「無檔案啟用」) 。 下列範例示範如何設定啟用端點:

<configuration>

  <system.serviceModel>

    <serviceHostingEnvironment>

      <serviceActivations>

        <add relativeAddress=「Greeting.svc」 service=「GreetingService」/>

      </serviceActivations>

    </serviceHostingEnvironment>

  </system.serviceModel>

</配置>

有了這個功能,現在可以使用 「Greeting.svc」 的相對路徑來啟用 GreetingService (相對於 Web 應用程式基底位址) 。 為了說明這一點,我在名為 「GreetingSite」 的電腦上建立了 IIS 應用程式,我指派給 「ASP.NET v4.0」 應用程式集區,並將它對應至包含上述web.config的 GreetingService 專案目錄。 現在,我可以直接流覽至 https://localhost/GreetingSite/Greeting.svc ,而不需要實際在磁片上擁有實體 .svc 檔案。 圖 9 顯示這在瀏覽器中的外觀。

圖 9:無檔案啟用範例

探索

我們將討論的下一個主要 WCF 4 功能是服務探索。 在某些特製化服務導向環境中,有一些服務,其執行時間位置為動態且不斷變更。 例如,請考慮不同類型服務啟用裝置的環境會持續加入和離開網路,作為整體商務解決方案的一部分。 處理此實境需要用戶端動態探索服務端點的執行時間位置。

WS-Discovery是一種 OASIS 規格,可定義 SOAP 型通訊協定,以動態探索執行時間的服務端點位置。 通訊協定可讓用戶端探查符合特定準則的服務端點,以擷取適當的候選項目清單。 然後,用戶端可以從探索到的清單選擇特定的端點,並使用其目前的執行時間端點位址。

WS-Discovery定義兩種主要作業模式:臨機操作模式和受控模式。 在臨機操作模式中,用戶端會藉由傳送多播訊息來探查服務。 此架構會為此臨機操作模式提供 UDP 多播機制。 符合探查的服務會直接回應用戶端。 為了將用戶端輪詢的需求降到最低,服務也可以在加入或離開網路時「宣告」自己,方法是將多播訊息傳送給可能「接聽」的用戶端。 臨機操作探索受限於用於多播訊息的通訊協定,在 UDP 的情況下,只有在本機子網上接聽的服務才能接收訊息。

透過受控服務探索,您會在網路上提供探索 Proxy,以「管理」可探索的服務端點。 用戶端會直接與探索 Proxy 通訊,以根據探查準則尋找服務。 探索 Proxy 需要可比對查詢的服務存放庫。 Proxy 如何填入這項資訊是實作詳細資料。 探索 Proxy 可以輕鬆地連線到現有的服務存放庫、它們可以使用端點清單預先設定,或者探索 Proxy 甚至可以接聽公告來更新其快取。 在受管理模式中,公告可能會由探索 Proxy 直接廣播給收件者。

.NET 4.0 架構提供實作您自己的探索 Proxy 所需的基類。 基類會抽象化探索通訊協定詳細資料,因此您可以只專注于您想要探索 Proxy 包含的邏輯。 例如,您只需要定義探索 Proxy 將執行的動作,以回應探查訊息、公告訊息和解析訊息。

WCF 4 提供WS-Discovery通訊協定的完整實作,並支援臨機操作和受控探索模式。 我們將簡短探討下列各項。

簡單服務探索

啟用服務探索最簡單的方式是透過臨機操作模式。 WCF 可讓您輕鬆地在服務主機應用程式中啟用服務探索,方法是提供一些標準探索端點和服務探索行為。 若要設定您的服務以進行探索,只需新增標準 「udpDiscoveryEndpoint」 端點,然後在服務上啟用 < serviceDiscovery > 行為。

以下是說明如何執行此動作的完整範例:

<configuration>

    <system.serviceModel>

      <services>

        <service name=「CalculatorService」>

          <endpoint binding=「wsHttpBinding」 contract=「ICalculatorService」 />

          <--新增標準 UDP 探索端點-->

          <端點名稱=「udpDiscovery」 kind=「udpDiscoveryEndpoint」/>

        </service>

      </服務>

      <behaviors>

        <serviceBehaviors>

          <behavior>

            <serviceDiscovery/ >< --啟用服務探索行為 -->

          </行為>

        </serviceBehaviors>

      </行為>

    </system.serviceModel>

</配置>

如此一來,您的服務會透過本機子網上的 UDP 來探索。 用戶端接著可以利用執行時間WS-Discovery來「探索」執行中服務的實際位址。 WCF 4 可讓用戶端透過 dynamicEndpoint 標準端點輕鬆完成此作業。

只要採用您用來連線到服務的現有用戶端端點,請移除位址並新增 kind=「dynamicEndpoint」 標籤。

<configuration>

    <system.serviceModel>

        <client>

          <endpoint

              name=「calculatorEndpoint」

              kind=「dynamicEndpoint」

              binding=「wsHttpBinding」

              contract=「ICalculatorService」>

          </端點>

        </客戶>

    </system.serviceModel>

</配置>

進行第一次服務呼叫時,用戶端會傳送多播查詢,尋找符合 ICalculatorService 合約的服務,並嘗試連線到一個服務。 各種設定可讓您微調 serach、調整探索系結和控制探索程式。 您也可以使用 DiscoveryClient 類別,以程式設計方式執行所有這些作業。

下列範例會進一步示範如何以程式設計方式使用 UdpDiscoveryEndpoint 來探索 ICalculatorService 端點,然後叫用它:

建立 DiscoveryClient

DiscoveryClient discoveryClient =

    new DiscoveryClient (新的 UdpDiscoveryEndpoint () ) ;

在指定的範圍內尋找 ICalculatorService 端點

FindCriteria findCriteria = new FindCriteria (typeof (ICalculatorService) ) ;

FindResponse findResponse = discoveryClient.Find (findCriteria) ;

只要挑選第一個探索到的端點

EndpointAddress address = findResponse.Endpoints[0]。位址;

建立目標服務用戶端

CalculatorServiceClient 用戶端 =

    new CalculatorServiceClient (「calculatorEndpoint」) ;

連線到探索到的服務端點

客戶。Endpoint.Address = address;

Console.WriteLine (「叫用 CalculatorService at {0} 」, address) ;

呼叫 [新增服務] 作業。

double result = client。新增 (100, 15.99) ;

Console.WriteLine (「Add ({0} , {1}) = {2} 」, 100, 15.99, result) ;

一旦用戶端程式擷取探索到的端點集合,就可以使用其中一個端點來實際叫用目標服務。 圖 10 顯示執行上述用戶端程式代碼的輸出,假設服務同時執行。 注意:在此範例中,探索用戶端上的尋找作業是同步的;探索也支援非同步尋找作業。

圖 10:執行探索用戶端程式代碼的輸出

探索端點時使用範圍

在上一個範例中,用戶端只會根據服務合約類型探查服務。 用戶端可以在傳送探索探查時提供其他範圍資訊來縮小探索結果範圍。 讓我們看看一個簡單的範例,以瞭解探索期間如何使用「範圍」。

首先,服務必須將一或多個範圍與要發行以供探索的每個端點產生關聯。 WCF 4 隨附 < endpointDiscovery > 行為,可用來定義一組您可以與端點定義建立關聯的範圍。 下列範例說明如何將兩個範圍與服務上定義的單一端點產生關聯:

<configuration>

    <system.serviceModel>

      <services>

        <service name=「CalculatorService」

                 behaviorConfiguration=「calculatorServiceBehavior」>

          <endpoint binding=「wsHttpBinding」

                    contract=「ICalculatorService」

                    behaviorConfiguration=「ep1Behavior」 />

          <端點名稱=「udpDiscovery」 kind=「udpDiscoveryEndpoint」/>

        </service>

      </服務>

      <behaviors>

        <serviceBehaviors>

          <行為名稱=「calculatorServiceBehavior」>

            <serviceDiscovery/>

          </行為>

        </serviceBehaviors>

        <endpointBehaviors>

          <行為名稱=「ep1Behavior」>

            <endpointDiscovery>

               <--與此端點行為相關聯的範圍 -->

              <scopes>

                <add scope=「 http://www.example.org/calculator" ;/>

                <add scope=「ldap:///ou=engineering,o=exampleorg,c=us」/>

              </範圍>

            </endpointDiscovery>

          </行為>

        </endpointBehaviors>

      </行為>

    </system.serviceModel>

</配置>

用戶端可以根據執行時間的特定範圍來探查服務端點。 它們可以藉由將目標範圍清單新增至您提供給 Find 作業的 FindCriteria 實例來執行此動作。 下列程式碼說明如何探索符合特定 LDAP 範圍的 ICalculatorService 端點:

...

建立 DiscoveryClient

DiscoveryClient discoveryClient = 新的 DiscoveryClient (「udpDiscoveryEndpoint」) ;

在指定的範圍內尋找 ICalculatorService 端點

Uri 範圍 = 新的 Uri (「ldap:///ou=engineering,o=exampleorg,c=us」) ;

FindCriteria findCriteria = new FindCriteria (typeof (ICalculatorService) ) ;

findCriteria.Scopes.Add (scope) ;

FindResponse findResponse = discoveryClient.Find (findCriteria) ;

...

利用範圍可讓您微調探索實作,讓用戶端更輕鬆地探索感興趣的特定服務端點。 探索也允許進一步自訂。 例如,服務可以將自訂 XML 中繼資料新增至端點。 此資訊會隨著用戶端傳送,以回應用戶端的查詢。

服務公告

WCF 4 也可讓您輕鬆地設定服務在啟動時「宣告」其端點。 這可讓「接聽」的用戶端在加入網路時立即瞭解新的服務端點,藉此減少用戶端所執行之探查 (和多播傳訊) 的數量。

您可以使用 serviceDiscovery > 行為,使用 < 公告端點來設定服務。 <serviceDiscovery > 行為可讓您定義服務將公開的公告端點集合。 在大部分情況下,您可以使用標準 「udpAnnouncementEndpoint」。

如果您想要回應用戶端所起始的探索探查,您仍然需要使用標準 「udpDiscoveryEndpoint」 來設定服務。 下列範例顯示一般設定:

<configuration>

  <system.serviceModel>

    <services>

      <service name=「CalculatorService」>

        <endpoint binding=「wsHttpBinding」 contract=「ICalculatorService」/>

        <endpoint kind=「udpDiscoveryEndpoint」/>

      </service>

    </服務>

    <behaviors>

      <serviceBehaviors>

        <behavior>

          <serviceDiscovery>

            <announcementEndpoints>

              <endpoint kind=「udpAnnouncementEndpoint」/>

            </announcementEndpoints>

          </serviceDiscovery>

        </行為>

      </serviceBehaviors>

    </行為>

  </system.serviceModel>

</配置>

有了此設定,服務會在線上時自行宣告,也會在離線時宣告。 為了利用這些公告,您必須特別設計用戶端在執行時間接聽這些公告。 您可以在實作WS-Discovery公告通訊協定的用戶端應用程式內裝載公告服務來執行此動作。

WCF 4 隨附一個名為 AnnouncementService 的類別,專為此目的所設計。 AnnouncementService 提供兩個事件處理常式:OnlineAnnouncementReceived 和 OfflineAnnouncementReceived。 用戶端應用程式只要使用 ServiceHost 裝載 AnnouncementService 的實例,並註冊這兩個事件的事件處理常式即可。

每當服務上線並宣告本身時,用戶端裝載的 AnnouncementService 將會收到「線上」公告,而 OnlineAnnouncementReceived 將會在用戶端中引發。 當服務離線時,它會傳送「離線」公告,而 OfflineAnnouncementReceived 將會在用戶端中引發。 下列說明裝載 AnnouncementService 的範例用戶端應用程式,並實作兩個公告事件的處理常式:

類別用戶端

{

    public static void Main ()

    {

        建立 AnnouncementService 實例

        AnnouncementService announcementService = new AnnouncementService () ;

        訂閱公告事件

        announcementService.OnlineAnnouncementReceived += OnOnlineEvent;

        announcementService.OfflineAnnouncementReceived += OnOfflineEvent;

        建立 AnnouncementService 的 ServiceHost

        using (ServiceHost announcementServiceHost =

            new ServiceHost (announcementService) )

        {

            接聽透過 UDP 多播傳送的公告

            announcementServiceHost.AddServiceEndpoint (

                new UdpAnnouncementEndpoint () ) ;

            announcementServiceHost.Open () ;

            Console.WriteLine (「接聽服務公告」。) ;

                    Console.WriteLine();

            Console.WriteLine (「按 < ENTER > 終止」。) ;

            Console.ReadLine();

        }

    }

    static void OnOnlineEvent (object sender, AnnouncementEventArgs e)

    {

                Console.WriteLine();

        Console.WriteLine (「收到來自 {0} :「的線上公告」

            e.EndpointDiscoveryMetadata.Address) ;

        PrintEndpointDiscoveryMetadata (e.EndpointDiscoveryMetadata) ;

    }

    static void OnOfflineEvent (物件傳送者,AnnouncementEventArgs e)

    {

                Console.WriteLine();

        Console.WriteLine (「收到來自 {0} :「的離線公告」

            e.EndpointDiscoveryMetadata.Address) ;

        PrintEndpointDiscoveryMetadata (e.EndpointDiscoveryMetadata) ;

    }

    ...

}

圖 11:接聽探索公告訊息

現在假設我執行此用戶端程式,並將它保持啟動並執行一段時間。 然後,我稍後會執行一些服務主機應用程式實例。 每次啟動時,我們都會在用戶端主控台視窗中看到出現「線上」宣告訊息。 當我關閉每個服務主機應用程式時,我們會在用戶端主控台視窗中看到「離線」公告訊息。 圖 11 顯示結果用戶端主控台視窗之後,執行我剛才描述的內容。

請記住,臨機操作探索模式僅適用于本機子網。 如果您想要使用超出區域網路界限的WS-Discovery,您必須轉換成受控探索模式。 WCF 4 也支援建置必要的受控探索元件。

受控服務探索

實作受控探索模式比臨機操作模式更相關,因為它需要您實作探索 Proxy 服務。 探索 Proxy 服務是會追蹤所有可用服務端點的元件。 在此範例中,我們會使用公告功能來更新探索 Proxy。 有許多其他方法可提供探索 Proxy 與相關的探索資訊,例如,您可以連接端點的現有資料庫,並從該處擷取資料。 那麼,您要如何實作探索 Proxy 服務?

WCF 4 隨附名為 DiscoveryProxy 的基類,您可以衍生自 以實作探索 Proxy 服務。 圖 12 顯示自訂探索 Proxy 服務實作的開始。 .NET 4 SDK 範例包含您參考的完整範例實作。 完成實作探索 Proxy 服務之後,您必須將它裝載于某個位置。

圖 12:實作自訂探索 Proxy 服務

[ServiceBehavior (InstanceCoNtextMode = InstanceCoNtextMode.Single,

    ConcurrencyMode = ConcurrencyMode.Multiple) ]

public 類別 MyDiscoveryProxy : DiscoveryProxyBase

{

    儲存 EndpointDiscoveryMetadata 的存放庫。

    您也可以改用資料庫或一般檔案。

    Dictionary < EndpointAddress、EndpointDiscoveryMetadata > onlineServices;

    public MyDiscoveryProxy ()

    {

        this.onlineServices =

            new Dictionary < EndpointAddress, EndpointDiscoveryMetadata > () ;

    }

    當 Proxy 收到 Hello 訊息時,會呼叫 OnBeginOnlineAnnouncement

    protected override IAsyncResult OnBeginOnlineAnnouncement (

        DiscoveryMessageSequence messageSequence, EndpointDiscoveryMetadata

        endpointDiscoveryMetadata、AsyncCallback 回呼、物件狀態)

    {

        這。AddOnlineService (endpointDiscoveryMetadata) ;

        會傳回新的 OnOnlineAnnouncementAsyncResult (回呼、狀態) ;

    }

    protected override void OnEndOnlineAnnouncement (IAsyncResult result)

    {

        OnOnlineAnnouncementAsyncResult.End (結果) ;

    }

    當 Proxy 收到 Bye 訊息時,會呼叫 OnBeginOfflineAnnouncement

    protected override IAsyncResult OnBeginOfflineAnnouncement (

        DiscoveryMessageSequence messageSequence, EndpointDiscoveryMetadata

        endpointDiscoveryMetadata、AsyncCallback 回呼、物件狀態)

    {

        這。RemoveOnlineService (endpointDiscoveryMetadata) ;

        會傳回新的 OnOfflineAnnouncementAsyncResult (回呼、狀態) ;

    }

    protected override void OnEndOfflineAnnouncement (IAsyncResult result)

    {

        OnOfflineAnnouncementAsyncResult.End (結果) ;

    }

    當 Proxy 收到探查要求訊息時,會呼叫 OnBeginFind

    protected override IAsyncResult OnBeginFind (

        FindRequestCoNtext findRequestCoNtext, AsyncCallback 回呼, 物件狀態)

    {

        這。MatchFromOnlineService (findRequestCoNtext) ;

        會傳回新的 OnFindAsyncResult (回呼、狀態) ;

    }

    protected override void OnEndFind (IAsyncResult result)

    {

        OnFindAsyncResult.End (結果) ;

    }

    ...

在此範例中,我只會在主控台應用程式中裝載 MyDiscoveryProxy 服務。  我將使用兩個端點來設定主機:探索端點和公告端點。 下列範例說明如何使用這兩個端點正確裝載 MyDiscoveryProxy 服務:

類別程式

{

    public static void Main ()

    {

        Uri probeEndpointAddress = 新的 Uri (「net.tcp://localhost:8001/Probe」) ;

        Uri announcementEndpointAddress =

            新的 Uri (「net.tcp://localhost:9021/Announcement」) ;

        ServiceHost proxyServiceHost = 新的 ServiceHost (新的 MyDiscoveryProxy () ) ;

        DiscoveryEndpoint discoveryEndpoint = 新的 DiscoveryEndpoint (

            new NetTcpBinding () , new EndpointAddress (probeEndpointAddress) ) ;

        discoveryEndpoint.IsSystemEndpoint = false;

        AnnouncementEndpoint announcementEndpoint = new AnnouncementEndpoint (

            new NetTcpBinding () , new EndpointAddress (announcementEndpointAddress) ) ;

        proxyServiceHost.AddServiceEndpoint (discoveryEndpoint) ;

        proxyServiceHost.AddServiceEndpoint (announcementEndpoint) ;

        proxyServiceHost.Open () ;

        Console.WriteLine (「Proxy 服務已啟動」。) ;

                Console.WriteLine();

        Console.WriteLine (「按 < ENTER > 終止服務」。) ;

                Console.WriteLine();

        Console.ReadLine();

        proxyServiceHost.Close () ;

    }

}

在探索 Proxy 服務啟動並執行之後,您可以設定服務,直接向探索 Proxy 服務宣告自己。 同樣地,您可以將用戶端應用程式設定為直接探查探索 Proxy 服務, (不會再) 多播傳訊。

您可以藉由在服務主機應用程式中建立 AnnouncementEndpoint 時指定探索 Proxy 的公告位址,將服務設定為直接宣告至探索 Proxy 服務。 下列範例示範如何完成此作業:

...

Uri baseAddress = 新的 Uri (「net.tcp://localhost:9002/CalculatorService/」 +

    Guid.NewGuid () 。ToString () ) ;

Uri announcementEndpointAddress = new Uri (「net.tcp://localhost:9021/Announcement」) ;

ServiceHost serviceHost = new ServiceHost (typeof (CalculatorService) , baseAddress) ;

ServiceEndpoint netTcpEndpoint = serviceHost.AddServiceEndpoint (

    typeof (ICalculatorService) 、new NetTcpBinding () 、string。空白) ;

建立指向託管 Proxy 服務的公告端點

AnnouncementEndpoint announcementEndpoint = new AnnouncementEndpoint (

    new NetTcpBinding () , new EndpointAddress (announcementEndpointAddress) ) ;

ServiceDiscoveryBehavior serviceDiscoveryBehavior = new ServiceDiscoveryBehavior () ;

serviceDiscoveryBehavior.AnnouncementEndpoints.Add (announcementEndpoint) ;

serviceHost.Description.Behaviors.Add (serviceDiscoveryBehavior) ;

serviceHost.Open () ;

...

然後,您可以在用戶端應用程式內建立 DiscoveryEndpoint 時指定探索 Proxy 的探查位址,以設定用戶端應用程式直接與探索 Proxy 服務通訊。 下列範例說明執行此動作的其中一種方式:

...

建立指向 Proxy 服務的探索端點。

Uri probeEndpointAddress = 新的 Uri (「net.tcp://localhost:8001/Probe」) ;

DiscoveryEndpoint discoveryEndpoint = 新的 DiscoveryEndpoint (

    new NetTcpBinding () , new EndpointAddress (probeEndpointAddress) ) ;

使用先前建立的 discoveryEndpoint 建立 DiscoveryClient

DiscoveryClient discoveryClient = 新的 DiscoveryClient (discoveryEndpoint) ;

尋找 ICalculatorService 端點

FindResponse findResponse = discoveryClient.Find (

    new FindCriteria (typeof (ICalculatorService) ) ) ;

...

現在讓我們逐步解說完整的範例。 首先,我會執行探索 Proxy 應用程式,讓探索 Proxy 服務可供使用。 然後,我會執行服務主機應用程式的實例,這會使用探索 Proxy 來宣告自己。 發生這種情況之後,我們將會看到列印至探索 Proxy 應用程式的主控台視窗的訊息, (請參閱圖 13) 。 這說明服務已成功向探索 Proxy 宣告,而探索 Proxy 會儲存新「線上」服務端點的相關資訊。 現在,如果我們執行上述的用戶端程式代碼,它會直接探查探索 Proxy,並擷取目前執行之目標服務的端點位址。

圖 13:執行時間探索 Proxy 服務的輸出

受控服務探索的優點在於,其可跨網路界限運作, (其以傳統服務呼叫) 為基礎,並減少探索解決方案內多播傳訊的需求。 此外,由於用戶端會經歷探索 Proxy 來尋找服務,因此服務本身不需要隨時啟動並執行,才能探索。

進階探索 Proxy 使用方式

WCF 程式設計模型提供實作探索 Proxy 的許多彈性。 接收公告是填入服務清單的其中一種方式;不過,這不是唯一的方法。 例如,如果您的環境已經包含服務存放庫,您可以輕鬆地在該存放區頂端建置探索 Proxy 外觀,以便在執行時間探索存放庫。

探索 Proxy 可以在臨機操作或管理模式中設定。 在受控模式中運作時,用戶端會使用公告、探查和解析,直接以單播方式與 Proxy 通訊。 Proxy 也會以單播方式將回應傳輸回傳送者。

如果以臨機操作模式操作,Proxy 可以接聽多播探索訊息,並直接回應傳送者。 在此臨機操作模式中,也可以特別設定 Proxy 來隱藏多播訊息。 也就是說,如果 Proxy 收到多播訊息,它會通知寄件者其存在狀態,並通知寄件者在 Proxy 上進一步查詢,藉此避免進一步的多播訊息。

如需這些進階探索案例的詳細資訊,請參閱 中的 http://www.oasis-open.org/committees/download.php/32184/WS-D-primer-wd-04.docx WS-Discovery Primer。

路由服務

在某些服務導向環境中,利用集中的「路由」服務,作為組織周圍散佈的實際商務服務代理程式或閘道通常很有用。 這會將取用者與實際商務服務分離,並讓路由節點內執行各種不同類型的中繼處理。

例如,某些環境會使用路由來實作所有傳入訊息必須通過的集中式安全性界限。 有些會使用以內容為基礎的路由技術,根據特定傳入訊息的內容來判斷要使用的目標服務。 其他人會使用路由來實作通訊協定橋接,藉此讓取用者能夠使用一組通訊協定進行通訊,而路由器使用一組不同的通訊協定來與目標服務通訊。 針對各種負載平衡或甚至服務版本控制技術使用路由也並不常見。

無論原因為何,現今建置大規模 SOA 解決方案時,「中繼路由」模式是常見的需求。 在 WCF 3.x 中,沒有正式支援路由。 雖然架構提供了實作您自己的路由服務所需的 API,但有許多工作可以正常執行。 MSDN Magazine 中已發佈數篇文章,示範如何達成此目的。

由於路由在幾天是常見的需求,因此 WCF 4 現在隨附架構中的官方「路由服務」,您只要在自己的解決方案中裝載和設定即可。

瞭解 RoutingService

WCF 4 隨附名為 RoutingService 的新類別,其提供一般 WCF 路由實作,以用於您的應用程式。 RoutingService 可以使用各種不同的傳訊模式,透過任何 WCF 支援的通訊協定來處理路由訊息,例如單向、要求-回應和雙工傳訊) 。 以下顯示 RoutingService 類別定義:

[ServiceBehavior (AddressFilterMode = AddressFilterMode.Any,

    InstanceCoNtextMode = InstanceCoNtextMode.PerSession,

    UseSynchronizationCoNtext = false,ValidateMustUnderstand = false) ,

 AspNetCompatibilityRequirements (RequirementsMode =

    AspNetCompatibilityRequirementsMode.Allowed) ]

public sealed 類別 RoutingService:// 合約允許不同的通訊模式

    ISimplexDatagramRouter、ISimplexSessionRouter、IRequestReplyRouter、

    IDuplexSessionRouter、IDisposable

{

    ... // 實作省略

}

如您所見,RoutingService 類別衍生自多個服務合約,以支援多個傳訊模式。 每個服務合約都支援不同的傳訊模式,包括適當時支援會話型通訊。

RoutingService 的整個用途是接收來自取用者的連入訊息,並將它們「路由」到適當的下游服務。 RouterService 會根據一組訊息篩選來評估每個傳入訊息,以判斷要使用的目標服務。 因此,身為開發人員,您可以藉由定義訊息篩選準則來控制路由行為,通常是在組態檔中。 目標服務可能位於與 RouterService 相同的電腦上,但不需要這些服務 – 也可能分散到網路,而且可能需要各種不同的通訊協定。

裝載 RoutingService

您可以在應用程式中裝載 RoutingService,就像任何其他 WCF 服務一樣。 您只要建立 ServiceHost 實例,並針對服務類型指定 RoutingService 即可。 呼叫 ServiceHost 實例上的 Open 之後,您的 RoutingService 將會準備好「路由」訊息,如下所示:

using System;

使用 System.ServiceModel;

使用 System.ServiceModel.Routing;

public static void Main ()

{

    建立 RoutingService 類型的 ServiceHost。

    使用 (ServiceHost serviceHost =

        new ServiceHost (typeof (RoutingService) ) )

    {

        嘗試

        {

            serviceHost.Open () ;

            Console.WriteLine (「路由服務目前正在執行中」。) ;

            Console.WriteLine (「按 < ENTER > 終止路由器」。) ;

            現在可以存取服務。

            Console.ReadLine();

            serviceHost.Close () ;

        }

        catch (CommunicationException)

        {

            serviceHost.Abort () ;

        }

    }

}

您也可以像任何其他服務一樣設定 RoutingService,以及定義路由篩選的位置。 首先,您必須使用一或多個端點進行設定。 定義路由端點時,您可以選擇 WCF 系結,以及上述 RoutingService 所實作的其中一個路由服務合約 (,例如 ISimplexDatagramRouter、IRequestReplyRouter 等) 。 如果您想要支援多個傳訊模式或 WCF 系結,您可以在 RoutingService 上公開多個端點。

下列範例說明如何使用四個路由端點來設定 RoutingService:兩個使用 BasicHttpBinding (單向和要求-回復) ,以及兩個使用 WSHttpBinding (單向和要求-回復) 。 請注意它就像設定任何其他 WCF 服務一樣:

<configuration>

  <system.serviceModel>

    <services>

      <--ROUTING SERVICE -->

      <服務行為Configuration=「routingData」

          name=「System.ServiceModel.Routing.RoutingService」>

        <主機>

          <baseAddresses>

            <add baseAddress=「 https://localhost:8000/routingservice/router" ;/>

          </baseAddresses>

        </主機>

        <!--

          定義及設定我們想要路由器接聽的端點和

          我們想要使用它的合約。 路由器提供的合約如下:

          ISimplexDatagramRouter、ISimplexSessionRouter、IRequestReplyRouter 和

          IDuplexSessionRouter。

         -->

        <endpoint address=「oneway-basic」

                  binding=「basicHttpBinding」

                  name=「onewayEndpointBasic」

                  contract=「System.ServiceModel.Routing.ISimplexDatagramRouter」 />

        <endpoint address=「oneway-ws」

                  binding=「wsHttpBinding」

                  name=「onewayEndpointWS」

                  contract=「System.ServiceModel.Routing.ISimplexDatagramRouter」 />

        <endpoint address=「twoway-basic」

                  binding=「basicHttpBinding」

                  name=「reqReplyEndpointBasic」

                  contract=「System.ServiceModel.Routing.IRequestReplyRouter」 />

        <endpoint address=「twoway-ws」

                  binding=「wsHttpBinding」

                  name=「reqReplyEndpointWS」

                  contract=「System.ServiceModel.Routing.IRequestReplyRouter」 />

      </service>

    </服務>

    ...

ISimplexDatagramRouter 和 IRequestReplyRouter 介面會定義一般單向和要求-回復服務合約定義,可與商務特定服務合約搭配使用。 以下顯示如何在 WCF 中定義這些介面:

[ServiceContract (Namespace=「 https://schemas.microsoft.com/netfx/2009/05/routing" ;,

    SessionMode = SessionMode.Allowed) ]

公用介面 ISimplexDatagramRouter

{

    [OperationContract (AsyncPattern = true, IsOneWay = true, Action = 「*」) ]

    IAsyncResult BeginProcessMessage (Message message, AsyncCallback 回呼,

        物件狀態) ;

    void EndProcessMessage (IAsyncResult 結果) ;

}

[ServiceContract (Namespace=「 https://schemas.microsoft.com/netfx/2009/05/routing" ;,

    SessionMode = SessionMode.Allowed) ]

公用介面 IRequestReplyRouter

{

    [OperationContract (AsyncPattern = true, IsOneWay= false, Action = 「*」,

        ReplyAction = 「*」) ]

    [GenericTransactionFlow (TransactionFlowOption.Allowed) ]

    IAsyncResult BeginProcessRequest (Message message, AsyncCallback 回呼,

        物件狀態) ;

    Message EndProcessRequest (IAsyncResult 結果) ;

}

上述端點組態會公開供取用者使用的路由端點。  用戶端應用程式會選擇其中一個端點在其用戶端程式代碼內使用,並將所有服務調用直接導向至 RoutingService。 當 RoutingService 透過其中一個端點接收訊息時,它會評估路由訊息篩選,以判斷要轉送訊息的位置。

使用訊息篩選設定 RoutingService

您可以透過程式碼或組態篩選來設定 RoutingService,就像 WCF) 中其他專案一樣 (。 WCF 4 提供 RoutingBehavior 來管理路由訊息篩選。 您必須先在 RouterService 上啟用 RoutingBehavior,然後指定您想要與 RoutingService 這個特定實例搭配使用的篩選資料表名稱:

<configuration>

  <system.serviceModel>

    ...

    <behaviors>

      <serviceBehaviors>

        <behavior name=「routingData」>

          <serviceMetadata HTTPGetEnabled=「True」/>

          <--定義路由行為,並指定篩選資料表名稱 -->

          <routing filterTableName=「filterTable1」 />

        </行為>

      </serviceBehaviors>

    </行為>

    ...

如果您查看先前使用端點設定 RoutingService 的範例,您會看到我們已透過 behaviorConfiguration 屬性將 「routingData」 行為套用至服務。 接下來,我們需要定義名為 「filterTable1」 的篩選資料表。

不過,在我們可以定義篩選資料表之前,我們需要目標服務的端點定義,才能路由傳送至此資料表。 您會在 WCF < 用戶端 > 組態區段中定義這些目標端點,因為 RoutingService 基本上是在將訊息轉送至目標服務時是「用戶端」。 下列範例示範如何定義我們可以路由傳送至的兩個目標端點:

<configuration>

    ...

    <--定義我們想要路由器與其通訊的用戶端端點。

         這些是路由器將傳送訊息的目標目的地。 -->

    <client>

      <endpoint name=「CalculatorService1」

       address=「 https://localhost:8000/servicemodelsamples/calcservice1" ;

       binding=「wsHttpBinding」 contract=「*」 />

      <endpoint name=「CalculatorService2」

       address=「 https://localhost:8001/servicemodelsamples/calcservice2" ;

       binding=「wsHttpBinding」 contract=「*」 />

    </客戶>

    ...

現在我們可以定義實際的篩選資料表,以判斷執行時間的路由邏輯。 您可以在 filterTables > 元素內 < 定義篩選資料表專案。 filterTable > 中的每個 < 專案都會定義路由「篩選」與目標端點之間的對應。 您可以在 filter 元素內 < 定義您想要使用的「篩選」– 每個 < 篩選 > 專案都會指定您想要搭配篩選特定資料使用的篩選類型 (,例如動作值、XPath 運算式等 >) 。

下列範例說明如何使用對應至 CalculatorService1 端點的單一篩選準則來設定篩選資料表。 在此情況下,「MatchAll」 篩選準則會符合所有傳入訊息:

<configuration>

    ...

    <--ROUTING 區段 -->

    <routing>

      <--定義我們想要路由器使用的篩選。 -->

      <filters>

        <filter name=「MatchAllFilter1」 filterType=「MatchAll」 />

      </過濾 器>

      <--定義包含 matchAll 篩選準則的篩選資料表 -->

      <filterTables>

        <filterTable name=「filterTable1」>

            <--將篩選對應至先前定義的用戶端端點。

                 將符合此篩選條件的訊息傳送至這個目的地。 -->

          <add filterName=「MatchAllFilter1」 endpointName=「CalculatorService1」 />

        </filterTable>

      </filterTables>

    </路由>

  </system.serviceModel>

</配置>

我們可以執行路由服務主機應用程式、CalculatorService1 主機應用程式,以及設計用來將訊息傳送至其中一個路由器端點的用戶端,來確認路由是否正常運作。 當我們執行用戶端時,應該會看到訊息在中繼 RoutingService「路由傳送」之後抵達 CalculatorService 1, (請參閱圖 14、圖 15 和圖 16) 。

圖 14:RoutingService 主機應用程式

圖 15:以 RoutingService 為目標的用戶端 https://localhost:8000/routingservice/router

圖 16:目標服務 (CalculatorService1)

訊息篩選和內容型路由

WCF 隨附數個內建的 MessageFilter 類別,您可以搭配路由訊息篩選準則來檢查傳入訊息的內容。

例如,WCF 提供 ActionMessageFilter,可讓您比對特定WS-Addressing 「action」 值。 WCF 也提供 EndpointAddressMessageFilter、EndpointNameMessageFilter 和 PrefixEndpointAddressMessageFilter,讓您符合特定的端點詳細資料。 其中一個最有彈性的是 XPathMessageFilter,可讓您針對傳入訊息評估 XPath 運算式。 所有這些篩選準則都可讓您在解決方案內執行以內容為基礎的路由。

除了這些內建的 MessageFilter 類型之外,WCF 4 也可讓您定義自訂訊息篩選,這會發生為 RoutingService 的主要擴充點之一。

讓我們看看根據動作值執行以內容為基礎的路由的範例。 假設我們想要將 CalculatorService 作業的一半路由至 CalculatorService1,另一半則路由傳送至 CalculatorService2。 您可以定義每個不同 CalculatorService 動作值的篩選,並將其一半對應至每個目標服務端點來完成此動作,如下所示:

<configuration>

    ...

    <--ROUTING 區段 -->

    <routing>

      <--定義我們想要路由器使用的篩選。 -->

     <filters>

        <filter name=「addFilter」 filterType=「Action」

         filterData=「 http://Microsoft.Samples.ServiceModel/ICalculator/Add" ;/>

        <filter name=「subFilter」 filterType=「Action」

         filterData=「 http://Microsoft.Samples.ServiceModel/ICalculator/Subtract" ;/>

        <filter name=「mulFilter」 filterType=「Action」

         filterData=「 http://Microsoft.Samples.ServiceModel/ICalculator/Multiply" ;/>

        <filter name=「divFilter」 filterType=「Action」

         filterData=「 http://Microsoft.Samples.ServiceModel/ICalculator/Divide" ;/>

      </過濾 器>

      <filterTables>

        <filterTable name=「filterTable1」>

          <add filterName=「addFilter」 endpointName=「CalculatorService1」/>

          <add filterName=「subFilter」 endpointName=「CalculatorService2」/>

          <add filterName=「mulFilter」 endpointName=「CalculatorService1」/>

          <add filterName=「divFilter」 endpointName=「CalculatorService2」/>

        </filterTable>

      </filterTables>

    </路由>

  </system.serviceModel>

</配置>

現在,當我們執行解決方案並執行叫用所有四項作業的用戶端時,我們會在每個服務主控台視窗中看到一半的作業, (請參閱圖 17 和圖 18) 。

圖 17:CalculatorService1 的輸出

圖 18:CalculatorService2 的輸出

XPathMessageFilter 可讓您更有彈性,因為您可以提供各種不同的 XPath 運算式,以針對傳入訊息進行評估。 您的 XPath 運算式可以評估傳入訊息的任何部分,包括 SOAP 標頭或 SOAP 主體。 這可讓您在建置以內容為基礎的訊息篩選時有大量的彈性。 為了瞭解 XPathMessageFilter 的機制,下列說明如何使用 XPath 運算式重寫最後一個範例:

<configuration>

    ...

    <--ROUTING 區段 -->

    <routing>

      <--定義我們想要路由器使用的篩選。 -->

     <filters>

        <filter name=「addFilter」 filterType=「XPath」

                filterData=「/s:Envelope/s:Header/wsa:Action =

                'http://Microsoft.Samples.ServiceModel/ICalculator/Add'"/>

        <filter name=「subFilter」 filterType=「XPath」

                filterData=「/s:Envelope/s:Header/wsa:Action =

                'http://Microsoft.Samples.ServiceModel/ICalculator/Subtract'"/>

        <filter name=「mulFilter」 filterType=「XPath」

                filterData=「/s:Envelope/s:Header/wsa:Action =

                'http://Microsoft.Samples.ServiceModel/ICalculator/Multiply'"/>

        <filter name=「divFilter」 filterType=「XPath」

                filterData=「/s:Envelope/s:Header/wsa:Action =

                'http://Microsoft.Samples.ServiceModel/ICalculator/Divide'"/>

      </過濾 器>

      <namespaceTable>

        <add prefix=「s」 namespace=「 http://www.w3.org/2003/05/soap-envelope" ; />

        <add prefix=「wsa」 namespace=「 http://www.w3.org/2005/08/addressing" ; />

      </namespaceTable>

      <filterTables>

        <filterTable name=「filterTable1」>

          <add filterName=「addFilter」 endpointName=「CalculatorService1」/>

          <add filterName=「subFilter」 endpointName=「CalculatorService2」/>

          <add filterName=「mulFilter」 endpointName=「CalculatorService1」/>

          <add filterName=「divFilter」 endpointName=「CalculatorService2」/>

        </filterTable>

      </filterTables>

    </路由>

  </system.serviceModel>

</配置>

請注意 filterData 屬性包含將針對傳入訊息評估的 XPath 運算式, (運算式只會檢查此範例中的動作值) 。 請注意,我如何使用 namespaceTable > 元素定義一組命名空間前置詞系結 < 。 如果您想要在 XPath 運算式中使用命名空間前置詞,就像我上述一樣,這是必要的。 使用此組態重新執行解決方案會產生與 (之前相同的結果,請參閱圖 17 和圖 18) 。

每當您需要根據自訂 SOAP 標頭來路由訊息,或根據 SOAP 訊息主體內找到的內容來路由傳送訊息時,您將需要使用這個 XPath 篩選技術。

通訊協定橋接

在先前的範例中,我們使用相同的 WCF 系結 (WSHttpBinding) 在用戶端與路由器之間,以及在路由器與目標服務之間通訊。 RoutingService 能夠跨大部分 WCF 系結橋接通訊。 例如,您可能想要設定路由器,讓用戶端透過 WSHttpBinding 與其通訊,但路由器會使用 NetTcpBinding 或 NetNamedPipeBinding 與下游目標服務通訊。

讓我們看看如何設定 RoutingService 來處理此案例。 我們會讓 RoutingService 端點設定與上述設定相同,這可讓取用者透過 BasicHttpBinding 或 WSHttpBinding 與 RoutingService 通訊。 但現在,我們將變更目標服務的用戶端端點定義,以使用 NetTcpBinding 和 NetNamedPipeBinding,如下所示:

<configuration>

    ...

    <--定義我們想要路由器與其通訊的用戶端端點。

         這些是路由器將傳送訊息的目標目的地。 -->

    <client>

      <endpoint name=「CalculatorService1」

       address=「net.tcp://localhost:8001/servicemodelsamples/calcservice1」

       binding=「netTcpBinding」 contract=「*」 />

      <endpoint name=「CalculatorService2」

       address=「net.pipe://localhost/servicemodelsamples/calcservice2」

       binding=「netNamedPipeBinding」 contract=「*」 />

    </客戶>

    ...

當然,我們必須更新 CalculatorService1 和 CalculatorService2 應用程式,以支援相容的 NetTcpBinding 和 NetNamedPipeBinding 端點。 有了此設定,取用者可以使用 BasicHttpBinding/WSHttpBinding 與 RoutingService 通訊,而路由器會使用 NetTcpBinding 或 NetNamedPipeBinding 與目標服務通訊,視訊息要路由傳送到的服務而定。

錯誤手部和容錯

RoutingService 也提供內建機制來處理執行時間通訊錯誤,並支援基本層級的容錯。 定義篩選資料表時,如果與初始目標端點通訊,路由服務將會使用的不同替代端點清單會導致錯誤。 這基本上可讓您有「備份」端點的清單。

下列範例說明如何在 backupLists > 元素內 < 定義備份端點清單,以便與篩選資料表專案產生關聯:

<configuration>

    ...

    <--ROUTING 區段 -->

    <routing>

      ... <!-- 定義我們想要路由器使用的篩選。 -->

      <filterTables>

        <filterTable name=「filterTable1」>

          <add filterName=「addFilter」 endpointName=「CalculatorService1」

               alternateEndpoints=「backupEndpoints」/>

          <add filterName=「subFilter」 endpointName=「CalculatorService1」

               alternateEndpoints=「backupEndpoints」/>

          <add filterName=「mulFilter」 endpointName=「CalculatorService1」

               alternateEndpoints=「backupEndpoints」/>

          <add filterName=「divFilter」 endpointName=「CalculatorService1」

               alternateEndpoints=「backupEndpoints」/>

        </filterTable>

      </filterTables>

      <backupLists>

        <backupList name=「backupEndpoints」>

          <add endpointName=「CalculatorService2」/>

        </backupList>

      </backupLists>

    </路由>

  </system.serviceModel>

</配置>

請注意,我們在此範例中將所有篩選資料表專案設定為轉送至 CalculatorService1 的方式,因為 CalculatorService2 現在是我們的「備份」端點,只有在 CalculatorService1 產生 TimeoutException、CommunicationException 或衍生例外狀況類型時才會使用。 例如,如果我再次執行解決方案並關閉 CalculatorService1,然後執行用戶端程式,我們將會看到所有訊息最後都會出現在 CalculatorService2 中。 請務必再次注意,所有路由設定都可以在主應用程式程式碼中動態執行。

多播路由行為

RoutingService 也支援以「多播」方式自動將特定傳入訊息路由傳送至多個目的地。 當傳入訊息符合已設定篩選資料表中找到的多個篩選時,RoutingService 會自動將訊息路由傳送至與「相符」篩選相關聯的每個目標端點。

下列範例顯示使用相同萬用字元篩選設定的兩個路由專案,其符合所有傳入訊息:

<configuration>

    ...

    <--ROUTING 區段 -->

    <routing>

      <--定義我們想要路由器使用的篩選。 -->

     <filters>

        <filter name=「wildcardFilter」 filterType=「MatchAll」 />

      </過濾 器>

      <filterTables>

        <filterTable name=「filterTable1」>

          <add filterName=「wildcardFilter」 endpointName=「CalculatorService1」/>

          <add filterName=「wildcardFilter」 endpointName=「CalculatorService2」/>

          <add filterName=「wildcardFilter」 endpointName=「CalculatorService3」/>

        </filterTable>

      </filterTables>

    </路由>

  </system.serviceModel>

</配置>

有了此設定,不論訊息中找到什麼,每個傳入的 SOAP 訊息都會自動路由傳送至所有目標端點。

此多播行為是由通訊協定橋接和上一節所討論的錯誤處理功能所組成。 唯一的問題是多播僅適用于單向或雙工通訊,但不適用於要求-回應通訊,因為基礎系統必須維持連出要求與連入回應之間的一對一比率。

改善的 REST 支援

WCF 4 隨附數項新功能,在使用 WCF 建置 RESTful 服務時會很有用。  這組功能現在稱為 WCF WebHttp 服務。 其中包括自動說明頁面的支援,可描述取用者的 RESTful 服務、簡化的 HTTP 快取、訊息格式選取、REST 易記的例外狀況、ASP.NET 路由整合、一些新的 Visual Studio 專案範本等等。 我們在這裡不會有空間詳細說明所有這些功能,但我將會提供以下幾個我的最愛的快速簡介,以及其他部分詳細資訊的連結。

其中許多功能最初是由 WCF REST 入門套件去年引進,現在已將其納入官方架構。 您未來可能會看到更多 WCF REST 入門套件功能。

自動說明頁面

在 WCF 4 中使用 WebServiceHost 類別時,您的 RESTful 服務會自動享有自動說明頁面功能的優點。 這在缺少 WSDL 中繼資料和用戶端程式代碼產生的情況下使用 REST 時,這是一個相當必要的新增功能,讓取用者更容易瞭解如何開始使用您的服務,因此通常最好啟用這項新功能。

當您使用 WebServiceHost 類別來裝載服務時,它會使用 WebHttpBehavior 自動設定您的服務,並在基底 HTTP 位址) 新增以 WebHttpBinding 設定的預設 HTTP 端點 (。 從 WCF 4 開始,WebHttpBehavior 類別隨附 HelpEnabled 屬性,可控制主機內是否啟用新的說明頁面。 下列組態範例示範如何啟用特定 REST 端點的自動說明頁面功能:

<configuration>

  <system.serviceModel>

    <serviceHostingEnvironment aspNetCompatibilityEnabled=「true」 />

    <behaviors>

      <endpointBehaviors>

        <行為名稱=「HelpBehavior」>

          <webHttp helpEnabled=「true」 />

        </行為>

      </endpointBehaviors>

    </行為>

    <services>

      <service name=「CounterResource」>

        <endpoint behaviorConfiguration=「HelpBehavior」

                  binding=「webHttpBinding」

                  contract=「CounterResource」 />

      </service>

    </服務>

  </system.serviceModel>

</配置>

您可以流覽至服務基底位址並附加至 URL 結尾的 「說明」,以查看說明頁面, (請參閱圖 19) 。

圖 19:RESTFul 服務的自動說明頁面

說明頁面提供以 [WebGet] 或 [WebInvoke] 批註的每個作業,並提供人類可讀的描述,並針對每個作業提供描述 URI 範本、支援的 HTTP 作業和支援的訊息格式,基本上取用者必須知道的所有專案 (請參閱圖 20) 。 您也可以將 [Description] 屬性套用至每個作業,以提供更易記的描述。

針對每個要求/回應,說明頁面也會提供 XML 架構和對應的範例 XML 實例,供取用者用來與服務整合。 取用者可以使用架構來產生適當的用戶端可序列化類型,也可以直接檢查範例 XML 檔,以手動判斷如何撰寫適當的 XML 處理常式代碼。 這兩種方法都很有用。

圖 20:特定作業的自動說明頁面

這個新的說明頁面功能會自動讓您的 RESTful 服務更容易探索,最終讓其他人更容易使用。 您的取用者可以探索服務的 URI 設計、支援的 HTTP 作業和要求/回應格式,而您的描述一律會與您的 WCF 程式碼保持同步,類似于使用 ASP.NET Web 服務的方式。

HTTP 快取支援

REST 的主要潛在優點之一是 HTTP 快取。 不過,為了瞭解該優點,您必須利用要求和回應訊息中的各種 HTTP 快取標頭。 您可以透過 WebOperationCoNtext 實例手動存取要求/回應標頭,以在 WCF 服務作業內達成此目的,但無法正常執行。

因此,WCF 4 隨附更簡單的模型,可用來透過 [AspNetCacheProfile] 屬性來控制快取,您可以宣告方式套用至 GET 作業。 此屬性可讓您為每個作業指定 ASP.NET 快取設定檔名稱,並在幕後有快取偵測器 (CacheingParameterInspector) ,負責處理所有基礎 HTTP 快取詳細資料。

[AspNetCacheProfile] 實作是以標準 ASP.NET 輸出快取機制為基礎。 下列範例示範如何將 [AspNetCacheProfile] 屬性套用至 [WebGet] 作業:

...

[AspNetCacheProfile (「CacheFor60Seconds」) ]

[WebGet (UriTemplate=XmlItemTemplate) ]

[OperationContract]

public Counter GetItemInXml ()

{

    return HandleGet () ;

}

...

如此一來,您必須在web.config檔案中定義名為 「CacheFor60Seconds」 的 ASP.NET 輸出快取設定檔。 下列web.config示範如何完成此作業:

<configuration>

  <system.web>

    <緩存>

      <outputCacheSettings>

        <outputCacheProfiles>

          <add name=「CacheFor60Seconds」 duration=「60」 varyByParam=「format」 />

        </outputCacheProfiles>

      </outputCacheSettings>

    </緩存>

  </system.web>

  <system.serviceModel>

    <serviceHostingEnvironment aspNetCompatibilityEnabled=「true」 />

  </system.serviceModel>

</配置>

請注意,ASP.NET 快取設定檔如何設定為快取輸出 60 秒,並設定為以「格式」查詢字串變數變更快取。 這很重要,因為有問題的服務同時支援 XML 和 JSON,您透過格式變數控制 (例如 「?format=json」) 。 服務必須獨立快取 XML 和 JSON 回應。

此組態檔也說明如何啟用 ASP.NET 相容性模式,當您想要利用各種 ASP.NET 功能,例如輸出快取時,就需要此模式。

[AspNetCacheProfile] 屬性可讓您更輕鬆地利用 HTTP 快取,而不需要您直接使用 HTTP 快取標頭。 基礎 WCF 行為會負責在回應中插入 HTTP 快取控制、日期、到期和不同 HTTP 標頭,用戶端也可以利用這些行為來判斷未來來回行程的適當快取語意。

郵件格式選取

如果您回顧圖 19,您會發現 CounterResource 服務同時支援 GET、POST 和 PUT 作業的 XML 和 JSON 格式。 這自 WCF 3.5 到 [WebGet] 和 [WebInvoke] 屬性上找到的 RequestFormat 和 ResponseFormat 屬性都可能。

我在 CounterResource 服務中完成此作業,方法是為每個版本定義個別的作業合約,一個用於 XML 版本,另一個用於 JSON 版本,如下所示:

...

[WebGet (UriTemplate=「」) ]

[OperationContract]

public Counter GetItemInXml ()

{

    return HandleGet () ;

}

[WebGet (UriTemplate = 「?format=json」, ResponseFormat=WebMessageFormat.Json) ]

[OperationContract]

public Counter GetItemInJson ()

{

    return HandleGet () ;

}

...

這很抱歉,對於每個邏輯作業,如果您想要同時支援 XML 和 JSON 格式,則需要兩個作業合約。 在 CounterResource 服務的情況下,我必須有六個作業合約,才能同時支援 GET、POST 和 PUT 作業的 XML 和 JSON。

WCF 4 可藉由根據 HTTP「Accept」 標頭提供自動格式選取的支援,讓此案例更容易處理,這是更好的做法。 讓我們說明運作方式。

首先,我們只需要每個邏輯作業的單一作業合約:

[WebGet (UriTemplate=「」) ]

[OperationContract]

public Counter GetItem ()

{

    return HandleGet () ;

}

然後,我們會在標準 WebHttpEndpoint 上自動選取格式,如下所示:

<configuration>

  <system.serviceModel>

    <standardEndpoints>

      <webHttpEndpoint>

        <--「」 標準端點用於自動建立 Web 端點。 -->

        <standardEndpoint name=「」 helpEnabled=「true」

            automaticFormatSelectionEnabled=「true」/>

      </webHttpEndpoint>

    </standardEndpoints>

  </system.serviceModel>

</配置>

有了這個功能,服務現在就能夠處理並傳回 XML 或 JSON 訊息。 它會先檢查要求訊息中找到的 HTTP Accept 標頭,找出要使用的格式。 如果無法運作,它會使用與要求訊息相同的訊息格式,假設它是 XML 或 JSON,否則它會針對特定作業使用預設格式。

現在,用戶端可決定要透過 HTTP 內容類型和接受標頭使用的格式。 Content-Type 標頭會指定要求訊息中的格式,而 Accept 標頭則表示用戶端從服務「接受」回傳的格式。 例如,如果用戶端想要從服務接收 JSON,它應該在要求中指定下列 HTTP Accept 標頭:

Accept: application/json

這在任何 HTTP 用戶端程式設計模型中都很容易完成,而且使用 Fiddler 之類的工具也很容易完成。 圖 21 顯示如何使用 Fiddler 從新的和改善的服務中取得 JSON。

圖 21:使用 HTTP 接受標頭擷取 JSON

WCF 4 也提供新的 API,可讓您輕鬆地在執行時間明確指定訊息格式。 下列程式碼示範如何藉由撰寫程式碼來擴充 GetItem 作業,以先尋找「格式」查詢字串參數,並據此明確設定回應格式。 如果找不到「format」 參數,它只會依賴 Accept 標頭,就像之前一樣。

[WebGet (UriTemplate=「」) ]

[OperationContract]

public Counter GetItem ()

{

    如果已指定格式查詢字串參數,則為

    將回應格式設定為該格式。 如果沒有,則為

    查詢字串參數存在,將會使用 Accept 標頭

    string formatQueryStringValue =

       WebOperationCoNtext.Current.IncomingRequest.UriTemplateMatch.QueryParameters[

       「format」];

    如果 (!string,則為 。IsNullOrEmpty (formatQueryStringValue) )

    {

        如果 (formatQueryStringValue.Equals (「xml」,

           System.StringComparison.OrdinalIgnoreCase) )

        {

            WebOperationCoNtext.Current.OutgoingResponse.Format = WebMessageFormat.Xml;

        }

        else if (formatQueryStringValue.Equals (「json」,

           System.StringComparison.OrdinalIgnoreCase) )

        {

            WebOperationCoNtext.Current.OutgoingResponse.Format = WebMessageFormat.Json;

        }

        else

        {

            擲回新的 WebFaultException < 字串 > (字串。格式 (「不支援的格式 ' {0} '」,

               formatQueryStringValue) 、HttpStatusCode.BadRequest) ;

        }

    }

    return HandleGet () ;

}

最後,這些新功能可讓您不再需要將訊息格式類型硬式編碼到 [WebGet] 和 [WebInvoke] 作業合約中,就像您在 WCF 3.5 中所做的一樣。

RESTful 錯誤處理

由於 RESTful 服務不會使用 SOAP,因此您不再需要標準 SOAP 錯誤機制,這可讓您在 .NET 例外狀況和 SOAP 錯誤訊息之間自動對應。 在 WCF 3.5 中建置 REST 服務時,每當您想要自訂傳回給用戶端的 HTTP 錯誤訊息時,您必須手動建構 HTTP 回應訊息。

在 WCF 4 中,您會發現名為 WebFaultException < T > 的新類別,可讓您更輕鬆地將「Web 錯誤」傳輸 (例如 HTTP 錯誤) 回取用者。 它的運作方式與 WCF 中的 FaultException < T > 非常類似,但您可以指定 HTTP 狀態碼和詳細資料類型,以提供更多詳細資料。

下列範例示範如何在使用者指定不支援的格式類型時傳回 HTTP 400 (不正確的要求) 錯誤 – 我只是針對詳細資料類型使用字串:

如果 (!string,則為 。IsNullOrEmpty (format) )

{

    如果 (格式,則為 。等於 (「xml」, System.StringComparison.OrdinalIgnoreCase) )

    {

        WebOperationCoNtext.Current.OutgoingResponse.Format = WebMessageFormat.Xml;

    }

    如果 (格式,則為 else。Equals (「json」, System.StringComparison.OrdinalIgnoreCase) )

    {

        WebOperationCoNtext.Current.OutgoingResponse.Format = WebMessageFormat.Json;

    }

    else

    {

        擲回新的 WebFaultException < 字串 > (字串。格式 (「不支援的格式 ' {0} '」,

           format) 、HttpStatusCode.BadRequest) ;

    }

}

這項新功能讓您只要擲回類似您一般在 SOAP 型服務中執行的例外狀況,即可更自然地傳回標準 HTTP 錯誤訊息。

將 WCF 與 ASP.NET 路由整合

在 WCF 4 和 ASP.NET 4 中找到的 URL 型路由功能之間有許多相似之處。 這兩者都可讓您執行相同的動作,基本上會將 URL 範本對應至類別上的方法。 不過,這兩個模型之間有一個顯著的差異。

WCF 4 方法會透過 [WebGet] 和 [WebInvoke] 屬性,將 URL 範本設計系結至在開發期間套用至類別定義的單一類別。 隨著服務隨著時間成長和演進,這可能會導致無法納入較社區塊的大型整合型設計。 另一方面,ASP.NET 4 方法會將路由邏輯與目標類別定義分離,因此可讓您視需要將服務路由對應到許多類別定義。

WCF 4 現在提供將 ASP.NET 路由引擎與您的 WCF 4 服務整合的能力,讓您能夠從 WCF 服務的 ASP.NET 路由模型獲益。

您可以利用新的 ServiceRoute 類別,在 Global.asax RegisterRoutes 方法中完成這項作業,讓您將 ASP.NET 路由對應至 WCF 服務類別:

private void RegisterRoutes ()

{

   WebServiceHostFactory Factory = 新的 WebServiceHostFactory () ;

   RouteTable.Routes.Add (新的 ServiceRoute (「Bookmarks」, factory,

      typeof (BookmarkService) ) ) ;

   RouteTable.Routes.Add (新的 ServiceRoute (「Users」, factory,

      typeof (UserService) ) ) ;

}

我呼叫 RouteTable.Routes.Add 兩次,為兩個不同的 WCF 服務類別新增路由。 第一個路由會將 「/Bookmarks」 對應至 BookmarkService 類別,而第二個路由會將 「/Users」 對應至 UserService 類別。 請注意這兩個路由如何設定為使用 WebServiceHostFactory。

現在,當我們在 WCF 服務類別定義上使用 [WebGet] 和 [WebInvoke] 屬性時,我們將使用相對路徑 ,而且它們會相對於此處指定的 ASP.NET 路由。

REST 專案範本

Visual Studio 2010 中的其中一個整齊功能是延伸模組管理員,您可以從 [工具] 存取此管理員 |延伸模組管理員。 延伸模組管理員可讓您和其他協力廠商使用簡單的按鈕,將新的專案範本、控制項和工具整合到 Visual Studio 2010 體驗中。 對於 Microsoft 產品小組而言,這很適合,因為它可讓您在 RTM 之後寄送專案,但仍讓 Microsoft 提供官方延伸模組。

如果您啟動延伸模組管理員並展開 [範本] 節點,您會發現名為 「WCF」 的子節點。 按一下 [WCF],您應該會看到四個新的 WCF REST 服務範本 – 每個 .NET 版本各有一個 (3.5 與 4.0) ,每個語言有一個 (C# 與 VB.NET) ,如圖 22 所示。

圖 22:擴充功能管理員中的新 WCF REST 專案範本

您可以選取這些新的專案範本,並將其下載至本機 Visual Studio 2010 安裝,並使用它們,就像任何其他專案類型一樣。 您會在 Visual C# 底下找到新的 REST 專案類型 |Web |WCF REST 服務應用程式 (假設您已安裝 C# 版本) 。

當您使用它來建立新專案時,您會發現它產生的 WCF REST 服務應用程式會使用我在此區段中醒目提示的大部分新 WCF 4 功能。

相關資訊

除了我們在這裡討論的內容之外,WCF 4 還隨附數個更進階的 REST 功能和新的 WCF API 延伸模組,可簡化處理自訂訊息格式 (而非 XML 或 JSON) 、使用新的 T4 功能傳回範本型「檢視」、實作條件式 GET 和 ETag 支援、實作開放式平行存取,以及產生輸出連結等專案。

如需所有這些新 WCF 4 功能的詳細資訊,包括我們在此沒有空間涵蓋的功能,請流覽至 .NET 端點部落格,並尋找在 .NET 4 仲介紹 WCF WebHttp 服務的專案。 小組提供了完整的 12 部分部落格系列,一次逐步解說每個新功能一個部落格文章,以及許多範例程式碼和逐步指示。

工作流程服務

在 .NET 4 中收到最注意的功能區域之一是「工作流程服務」。 Microsoft 已大量投資改善 WCF 與 WF 之間的整合,以提供豐富的宣告式程式設計模型來建置各種應用程式。

瞭解工作流程服務

WF 提供宣告式程式設計模型,以引發撰寫邏輯的抽象層級。 WF 最終會提供您撰寫自己語言的架構,方法是定義自己的商務域活動程式庫。 然後,它會提供視覺化設計工具,讓您將這些活動撰寫成程式,以及知道如何管理這些程式的執行時間。

WF 提供特別良好的模型,可用來實作長時間執行的應用程式,這些應用程式需要等候不同類型的外來事件發生,例如接收來自另一個系統的訊息。 其持續性模型只讓程式在實際執行時,才能夠有效率地使用系統資源。 當程式等候外來事件發生時,可以保存到資料庫,藉此釋放它所使用的系統資源。

圖 23:工作流程服務

讓 WF 與其他開發架構不同之處在于它提供這些功能,同時仍可讓您使用循序流程式控制制程式設計邏輯進行程式設計,這通常更容易供開發人員讀取和撰寫程式碼來瞭解。

如果您要建置本質上長時間執行的 WCF 服務,或可能受益于我剛才描述的一些其他優點,您可以使用 WF 工作流程來實作 WCF 服務。 您可以使用 WF 來協調取用其他外部服務的邏輯。

圖 23 說明這些概念。

雖然自 .NET 3.5 起可能,.NET 4 已大幅改善開發人員體驗,並提供 .NET 3.5 中遺漏的一些必要功能。

藉由結合 WCF 和 WF 的世界,即可充分利用這兩個世界。 您會獲得宣告式程式設計模型、開發人員易記的體驗,感謝 WF 設計工具、管理長時間執行服務的強大執行時間,以及 WCF 所提供的豐富通訊彈性。

建置您的第一個工作流程服務

為了讓您感覺新的工作流程服務開發人員體驗,我將逐步引導您完成使用 .NET 4 和新的 Visual Studio 2010 設計工具建置一個的完整範例。

如果您開啟 Visual Studio 2010,然後選取 [檔案] |新增專案,您會在 WCF 範本下找到新的工作流程專案,稱為「WCF 工作流程服務應用程式」。 我將會選取此專案範本,並為其命名 「HelloWorldWorkflowService」,然後建立新的專案。

圖 24:Visual Studio 2010 工作流程服務專案範本

建立之後,新的專案只會包含兩個檔案 :Service1.xamlx 檔案,其中包含宣告式工作流程服務定義,以及包含服務組態的Web.config檔案。

.NET 4 中新工作流程服務模型最吸引人的其中一件事,就是整個服務定義都可以在 XAML 中定義。   在我們的新專案中,Service1.xamlx 檔案包含服務定義,而實作只是以 XAML 為基礎的工作流程。 Web.config檔案包含 WCF 服務組態 – 這是您將定義工作流程服務端點和行為的位置。

圖 25 顯示 Visual Studio 2010 設計工具對 Service1.xamlx 檔案的外觀。 請注意,這只是標準工作流程設計工具,在此特定案例中,我們設計的工作流程將會作為 WCF 服務實作。 整合此工作流程定義與 WCF 的關鍵在於新的 WorkflowServiceHost 和一組 WCF「傳訊」活動 (請參閱圖 25 中的工具箱) 。

圖 25:在 Visual Studio 2010 中設計工作流程服務

此專案範本所提供的基本架構工作流程服務包含「接收」活動,後面接著「傳送」活動。 請注意,Receive 活動包含 OperationName 屬性,而且目前設定為 「GetData」。 它也具有 Content 屬性,可用來將傳入訊息系結至序列活動內定義的區域變數 –在此案例中,變數稱為「資料」,且其類型為 Int32 (請參閱圖 25 中工作流程設計服務底下的 [變數] 視窗) 。

「接收」活動也會設定為啟動服務,這表示傳入訊息可能會導致建立工作流程的新實例。 請注意,CanCreateInstance 屬性會在圖 25) 右側的屬性視窗內檢查。

讓我們稍微自訂此工作流程服務。 首先,我會將服務檔案名從 Service1.xamlx 變更為 HelloWorld.xamlx。 接下來,我會在適當的視窗中將服務的名稱變更為 「HelloWorldService」。 接下來,我會將 Receive 活動的 OperationName 屬性變更為 「SayHello」。 最後,我會在 Sequence 中定義名為 String 類型的 「personName」 的新變數。

然後,我會將 Receive 活動的 Content 屬性系結至 「personName」 變數,如圖 26 所示。 接下來,我將 Send 活動的 Content 屬性系結至格式為 「hello {0} !」 的字串 將 personName 變數插入預留位置。 然後,我會儲存產生的 Service1.xamlx 檔案。

圖 26:定義 Receive 活動的 Content 屬性

此時,請繼續開啟 HelloWorld.xamlx 檔案,並檢查其內容。 您可以用滑鼠右鍵按一下 方案總管 中的檔案,然後選取 [開啟方式...] 後面接著 「XML 編輯器」。 這可讓您查看服務的原始 XAML 定義。 請務必注意,XAML 定義代表完整的服務實作。 完全沒有 C# 程式碼。

在此範例中,我們將依賴預設 HTTP 端點,並假設我們已有標準服務行為,可自動啟用服務中繼資料,因此我們不需要任何 WCF 組態。

現在我們已準備好測試以 XAML 為基礎的工作流程服務。 這就像在 Visual Studio 2010 中按 F5 一樣簡單。 按 F5 會將工作流程服務載入 ASP.NET 開發伺服器,並導致 WCF 測試用戶端出現。 WCF 測試用戶端會自動連線到工作流程服務、下載 WSDL 中繼資料,並讓測試工作流程服務邏輯 (請參閱圖 27) 。

圖 27:測試工作流程服務

這說明工作流程服務基礎結構能夠藉由檢查工作流程定義中使用的傳送和接收活動,以及正在傳送和接收的訊息類型,動態產生服務的 WSDL 定義。

這會完成在 .NET 4 中建置第一個宣告式工作流程服務的簡單逐步解說。 我們發現服務實作在 XAML 中完全定義,而且您使用傳送/接收活動來建立工作流程內傳訊互動的模型。 我們也看到 .NET 4 隨附 .xamlx 檔案的裝載支援,因此不需要額外的 .svc 檔案。 我們將在下列各節中更詳細地深入探討每個區域。

裝載工作流程服務

.NET 4 隨附工作流程服務的新和改良裝載基礎結構。 您可以使用 WorkflowServiceHost,在 IIS/WAS 或您自己的應用程式中裝載工作流程服務。 .NET 4 裝載基礎結構提供管理長時間執行工作流程實例的必要部分,以及在有多個服務實例同時執行時相互關聯訊息。 .NET 4 也提供標準工作流程式控制制端點,以遠端系統管理工作流程實例。

裝載于 IIS/ASP.NET

我們在上一節中逐步解說的簡單 HelloWorldWorkflowService 範例說明如何在 IIS/ASP.NET 中裝載工作流程服務。 雖然我們已使用 ASP.NET 開發伺服器來測試服務,但它在 IIS 中使用 ASP.NET 4 個應用程式集區來運作相同。 .NET 4 會安裝 .xamlx 要求的必要處理常式,其會在幕後處理 WorkflowServiceHost 的建立,以及個別工作流程實例的啟用。

雖然我們在第一個範例中使用「零設定」方法,但您可以像任何其他 WCF 服務一樣,在Web.config內設定工作流程服務。 這是您要設定任何 WCF 端點和您想要使用的行為的位置。 您可以在工作流程服務上公開您想要的端點數目,但您應該考慮使用其中一個新的「內容」系結 (例如 BasicHttpCoNtextBinding、WSHttpCoNtextBinding 或 NetTcpCoNtextBinding) 來管理實例特定通訊。

下列範例說明如何使用兩個 HTTP 端點來設定工作流程服務:

<configuration>

  <system.serviceModel>

    <services>

      <service name=「HelloWorldWorkflowService」>

        <endpoint binding=「basicHttpCoNtextBinding」 contract=「IHelloWorld」/>

        <endpoint address=「ws」 binding=「wsHttpCoNtextBinding」

                  contract=「IHelloWorld」/>

      </service>

      ...

您也可以使用 WCF 行為來設定工作流程服務。 下列範例說明如何使用一些標準 WCF 行為來設定此工作流程服務:

<configuration>

  <system.serviceModel>

    <services>

      <service name=「HelloWorldWorkflowService」

        behaviorConfiguration=「HelloWorldWorkflowService.Service1Behavior」 >

        <endpoint binding=「basicHttpCoNtextBinding」 contract=「IHelloWorld」/>

        <endpoint address=「ws」 binding=「wsHttpCoNtextBinding」

                  contract=「IHelloWorld」/>

      </service>

     </服務>

    <behaviors>

      <serviceBehaviors>

        <行為名稱=「HelloWorldWorkflowService.Service1Behavior」>

          <serviceDebug includeExceptionDetailInFaults=「False」 />

          <serviceMetadata HTTPGetEnabled=「True」/>

        </行為>

      </serviceBehaviors>

      ...

除了這些標準 WCF 行為之外,.NET 4 還隨附一些新的 WF 特定行為,可控制工作流程持續性、工作流程追蹤和其他工作流程執行時間行為與您的工作流程服務搭配使用。 如需詳細資訊,請參閱 .NET 4 SDK 範例。

使用 WorkflowServiceHost 進行自我裝載

雖然 .NET 3.5 隨附 WorkflowServiceHost 類別來裝載工作流程服務,但必須重新設計它,才能在 .NET 4 中針對 WF 執行時間和程式設計模型進行所有變更。 因此,.NET 4 隨附 System.ServiceModel.Activities 元件中找到的新 WorkflowServiceHost 類別。

WorkflowServiceHost 類別可讓您輕鬆地在自己的應用程式中裝載工作流程服務,無論是主控台應用程式、WPF 應用程式或 Windows 服務。 下列程式碼片段說明如何在您自己的應用程式程式碼中使用 WorkflowServiceHost:

...

WorkflowServiceHost 主機 = 新的 WorkflowServiceHost (「HelloWorld.xamlx」,

    新 URI (「 https://localhost:8080/helloworld" ;) ) ;

主機。AddDefaultEndpoints () ;

主機。Description.Behaviors.Add (

    new ServiceMetadataBehavior { HttpGetEnabled = true }) ;

主機。Open () ;

Console.WriteLine (「主機已開啟」) ;

Console.ReadLine();

...

如您所見,無論您選擇在 IIS/ASP.NET 或您自己的應用程式中裝載工作流程服務,它們都如同任何 WCF 服務一樣容易裝載。

WorkflowControlEndpoint

裝載工作流程服務會負責初始化 WF 執行時間,並在啟動訊息通過其中一個公開端點時啟動新的工作流程實例。 除了這個基本裝載功能之外,也請務必從遠端系統管理這些執行中工作流程實例的組態和執行。 .NET 4 提供可在工作流程服務上公開的標準 WorkflowControlEndpoint,讓您輕鬆完成此作業。

下列範例說明如何在自訂裝載案例中,將標準工作流程式控制制端點新增至您的服務:

...

WorkflowServiceHost 主機 = 新的 WorkflowServiceHost (「HelloWorld.xamlx」,

    新 URI (「 https://localhost:8080/helloworld" ;) ) ;

主機。AddDefaultEndpoints () ;

WorkflowControlEndpoint wce = 新的 WorkflowControlEndpoint (

    new NetNamedPipeBinding () ,

    new EndpointAddress (「net.pipe://localhost/helloworld/WCE」) ) ;

主機。AddServiceEndpoint (wce) ;

主機。Open () ;

...

如果您要在 IIS/ASP.NET 中裝載,您可以新增標準 WorkflowControlEndpoint,如下所示:

<configuration>

  <system.serviceModel>

    <services>

      <service name=「HelloWorldWorkflowService」

          behaviorConfiguration=「HelloWorldWorkflowService.Service1Behavior」 >

        <endpoint address=「」 binding=「basicHttpCoNtextBinding」 contract=「IHelloWorld」/>

        <endpoint address=「ws」 binding=「wsHttpCoNtextBinding」 contract=「IHelloWorld」/>

        <endpoint address=「wce」 binding=「wsHttpBinding」 kind=「workflowControlEndpoint」/>

      </service>

      ...

WorkflowControlEndpoint 可讓您在不同的裝載環境中管理工作流程服務。 Windows Server AppFabric 可讓其中一個這類環境可供未來版本的 Windows Server 使用。

傳送/接收活動

.NET 3.5 隨附兩個傳訊活動 – 傳送和接收 – 用於使用 WCF 傳送和接收訊息,但其功能相當有限。 .NET 4 使用一些額外的功能來增強傳送和接收活動 (例如相互關聯) ,並新增更多傳訊活動 – SendReply 和 ReceiveReply ,以簡化模型化要求-回復作業。

當您在工作流程服務中使用傳送/接收活動時,基本上會使用這些活動來建立服務合約的模型,而服務合約將會透過 WSDL 定義公開給用戶端。 使用接收活動時,您會提供作業名稱,並將傳入訊息對應至 .NET 類型。 雖然到目前為止我們使用的是簡單的字串,但您也可以使用複雜的使用者定義資料合約。 讓我們更新 HelloWorldWorkflowService 以使用下列資料合約類型:

[DataContract (Namespace=「」) ]

public class Person

{

    [DataMember]

    公用字串識別碼;

    [DataMember]

    public string FirstName;

    [DataMember]

    public string LastName;

}

如果我們將此類別定義新增至 Web 專案並返回 HelloWorld.xamlx,我們可以接著定義名為 Person 類型的 「personMsg」 的新變數, (上面定義的資料合約) 。 然後我們可以透過 「Content」 屬性,將 ReceiveRequest 和 SendResponse 活動對應至 personMsg 變數。 若要這樣做,只要選取每個活動,然後按 [內容] 按鈕,即可顯示 [內容定義] 視窗。 然後,在 [訊息資料] 文字方塊中輸入 「personMsg」,然後在 [訊息類型] 文字方塊中輸入 「Person」。 您必須針對這兩個活動執行此動作。

傳送/接收活動也可讓您設定 WCF 服務的其他層面,例如作業名稱、服務合約名稱、動作、已知類型的集合、保護層級,以及要在執行時間使用之序列化程式 (例如 DataContractSerializer 與 XmlSerializer) 。 在 [傳送] 活動上,您也會指定目標端點詳細資料。 當您選取 [傳送/接收] 活動時,所有這些設定都可以透過屬性視窗進行設定。

有了這些變更,您可以再次測試 HelloWorld.xamlx,以查看 SayHello 作業現在已定義來接收並傳回 Person 類型的訊息。

定義Request-Reply作業

為了更輕鬆地建立要求-回復作業的模型,.NET 4 引進了一些新活動來模型化這些類型的互動。 其中一個是 SendReply 活動,您可以結合 Receive 活動,在服務內實作要求-回復作業。 另外還有 ReceiveReply 活動,您可以在工作流程內叫用外部服務時與傳送活動結合。

.NET 4 也隨附一些較高層級的活動,稱為 ReceiveAndSendReply 和 SendAndReceiveReply,您將在 Visual Studio 2010 工具箱中看到。 這些活動只會分別使用 SendReply/ReceiveReply 撰寫 Receive/Send,並方便使用。 當您將它們拖曳到工作流程設計介面上時,您會看到它們展開至包含傳送或接收活動的序列,後面接著適當的「回復」活動。

中的 [

為了在取用外部服務時更輕鬆,Visual Studio 2010 也提供「新增服務參考」功能,其運作方式就像您預期一樣,但除了產生用戶端 Proxy 類別定義之外,還會產生一組用戶端活動。 選取 [新增服務參考] 並指定 WSDL 定義的位址之後,Visual Studio 會下載 WSDL,並為 WSDL 定義中找到的每個作業產生自訂活動。

讓我們看看簡單的範例。 假設有一個 CalculatorService 與 Add、Subtract、Multiply 和 Divide 作業,我們想要從工作流程服務使用。 我們只要選取 [新增服務參考],並指定 CalculatorService WSDL 定義的位置,如圖 28 所示。

圖 28:新增服務參考

一旦按下 [確定] 以新增服務參考,Visual Studio 就會下載 WSDL 定義,並產生四個自訂活動,這些活動會出現在工具箱中。 您現在擁有可在工作流程內輕鬆使用的 Add、Subtract、Multiply 和 Divide 活動,以叫用外部服務。

Correlation

建置由長時間執行工作流程服務組成的系統時,通常會讓單一工作流程服務的許多實例同時執行,以等候相同的事件發生 (例如,在繼續) 之前等候特定訊息送達。 此案例的挑戰是您需要一種方式,將傳入訊息與正確的工作流程實例相互關聯。 一般而言,您判斷正確工作流程實例的方式是檢查傳入訊息的內容。

.NET 4 隨附複雜的內容型訊息相互關聯功能,您可以搭配我們剛才討論的傳送和接收活動使用。 這項功能的實作會圍繞所謂的「相互關聯控制碼」,這是 .NET 4 中另一個新概念。

若要開始使用相互關聯,您必須先定義 CorrelationHandle 類型的工作流程變數。 您可以將此變數視為聯結點,以便從 () 兩個不同的訊息, (由兩個不同的傳訊活動處理) 。 Send 和 Receive 活動都提供 CorrelatesWith 屬性來指定 CorrelationHandle 變數。

若要使多個傳送/接收活動所傳送的訊息相互關聯,您必須在所有想要參與相互關聯的活動中指定相同的相互關聯控制碼。 接著,在每個活動中,您會設定對應至該特定活動所處理之訊息的相互關聯索引鍵 (例如,某個訊息中的 SSN 元素可能會與其他訊息中的 CustomerId 元素相互關聯) 。 您可以使用針對訊息評估的 XPath 運算式來定義相互關聯索引鍵。

讓我們擴充 HelloWorldWorkflowService,以查看運作方式的範例。 目前,我們正在使用的範例會收到 Person 訊息,並將它傳回給用戶端。 讓我們在工作流程底部的 SendResponse 活動下方新增另一個 ReceiveAndSendReply 活動。 這樣做會新增包含另一個 Receive 的 Sequence,後面接著另一個 SendReply。 我會為 「接收」活動提供「完成」的作業名稱,而我們將要求用戶端在 Finish 訊息中提供問候語,我們將用來建構最終問候語回應。

換句話說,用戶端會先呼叫 SayHello 提供其完整名稱,以啟動新的工作流程服務實例。 然後,工作流程實例會等到用戶端呼叫完成提供問候語 (,這可能需要幾分鐘、小時或幾天的時間,工作流程才能保存) 。 一旦用戶端呼叫 Finish 提供問候語,工作流程就會使用問候語和原始名稱來建置問候語,並傳回它。 在此案例中,我們可以輕鬆地有多個執行中的工作流程實例,等待來電 Finish 作業,因此我們絕對需要將 Finish 訊息與上述 SayHello 訊息相互關聯。 因為這是這種情況,我需要將 「完成」的 Receive 活動與我們在 「SayHello」 活動上指定的相同相互關聯控制碼產生關聯, (nameHandle) 。

現在讓我們看看我們將相互關聯這兩個活動的訊息內容。 在此範例中,我將使用下列兩種資料合約類型來建立訊息的模型:

[DataContract (Namespace=「」) ]

public class Person

{

    [DataMember]

    public string FirstName { get; set; }

    [DataMember]

    public string LastName { get; set; }

}

[DataContract (Namespace = 「」) ]

public 類別 Greeting

{

    [DataMember]

    public string Salutation { get; set; }

    [DataMember]

    public string NameKey { get; set; }

}

「SayHello」 的接收活動已設定為使用 Person 訊息,而 「Finish」 的接收活動會設定為使用 Greeting 訊息。 我們將假設 LastName 值一律是唯一的,因此我們可以使用它作為相互關聯的唯一值。 因此,當用戶端傳送「Finish」 訊息時,它必須提供上述 「SayHello」 訊息中使用的相同 LastName 值。

現在讓我們看看如何設定相互關聯控制碼來設定此控制碼。 我已在名為 「handle」 的序列中定義 CorrelationHandle 變數。 我們需要做的第一件事是「初始化」「SayHello」 接收活動內的相互關聯控制碼。 因此,選取 [SayHello] 接收活動,然後按 [CorrelationInitializers] 文字方塊旁的按鈕。

在這裡,您需要從下拉式方塊中選取 [查詢相互關聯初始化運算式],然後您應該能夠選取查詢欄位的 「LastName」 屬性, (這應該會產生 「sm:body () /xg0:Person/xg0:LastName」 的 XPath 運算式,如圖 29) 所示。

然後,我們需要指定「完成」接收活動與相同控制碼上的相互關聯。 選取 [完成] [接收] 活動,然後按 [相互關聯] 按鈕。 然後為 「CorrelatesWith」 控制碼指定 「handle」,然後針對查詢欄位選取 「NameKey」, (請參閱圖 30) 。

圖 29:定義 「SayHello」 的相互關聯索引鍵

圖 30:定義「完成」的相互關聯索引鍵

這最後表示 Person 訊息中的 LastName 元素必須符合 Greeting 訊息中兩個不同的要求中的 NameKey 元素。 如此一來,工作流程基礎結構就能夠自動將訊息相互關聯,並根據 「LastName/NameKey」 ) 將傳入的「完成」訊息路由傳送至正確的工作流程實例 (。

最後的 SendReply 活動會將下列格式化字串傳回給呼叫端,包括來自原始 「personMsg」 和新 「greetingMsg」 的資訊:

String.Format (「 {0}{1}{2} !」,

   greetingMsg.Salutation, personMsg.FirstName, personMsg.LastName)

我將會使用 WCF 測試用戶端來測試相互關聯功能,藉由呼叫 SayHello 多次並使用不同的 FirstName 和 LastName 值來啟動數個工作流程實例。 執行此動作之後,我們應該有數個執行中的工作流程服務實例。 現在,我們可以呼叫 Finish 作業,指定其中一個 SayHello 訊息中使用的相同 LastName 值,而且應該會看到最後傳回給用戶端的問候語中使用的對應 FirstName 值, (請參閱圖 31) 。

這說明以內容為基礎的相互關聯運作,這是 .NET 4 隨附的吸引人的工作流程服務功能。 請務必注意,您也可以根據通道和通訊協定層級資料執行相互關聯,例如,使用通道或工作流程實例識別碼) 等.NET 3.5 (。 以內容為基礎的相互關聯是更有彈性且複雜的方法來執行相同的動作。

圖 31:測試以內容為基礎的相互關聯範例

這可讓我們結束工作流程服務涵蓋範圍。 我們只能在這份檔中暫存工作流程服務的介面,但您可以在 .NET 4 SDK 範例和線上找到的成長 MSDN 檔中找到其他資訊。 如您所見,WCF 和 WF 4 的組合會開啟全新的開發模型,以建置宣告式、長時間執行和非同步服務,以享有這兩個架構必須提供的最佳功能。

其他進階功能

除了我們在此檔中討論的所有專案之外,WCF 4 還隨附一些更進階的功能,您可能會發現這些功能很有用。 這些進階功能包括透過 DataContractResolver 改善的類型解析支援、使用 ReceiveCoNtext 處理競爭佇列取用者的能力、新的位元組資料流程編碼器,以及高效能的 ETW 型追蹤。

DataContractResolver 的類型解析

在 WCF 3.x 中,有稱為「已知類型」的類型解析功能。 在還原序列化期間,當序列化程式遇到與宣告類型不相同類型的實例時,它會檢查宣告的「已知型別」清單,以找出要使用的類型。 身為服務的作者,您可以使用 [KnownType] 或 [ServiceKnownType] 屬性來標注類型/方法,以定義可能的替代清單。 這項功能通常用來支援繼承和多型。

不幸的是,WCF 3.x 不會在執行時間執行這種類型的動態類型解析時,提供一種簡單的方式來覆寫 DataContractSerializer 所使用的類型對應演算法。 為了解決此問題,WCF 4 提供抽象的 DataContractResolver 類別,您可以衍生自 這個類別來實作自己的自訂類型解析演算法。  以下顯示如何開始使用:

類別 MyDataContractResolver :DataContractResolver

{

    元件元件;

    public MyDataContractResolver (Assembly 元件)

    {

        this.assembly = assembly;

    }

    用於還原序列化

    允許使用者將 xsi:type 名稱對應至任何類型

    public override Type ResolveName (string typeName, string typeNamespace,

        類型 declaredType、DataContractResolver knownTypeResolver)

    {

        ... // 實作您的對應

    }

    用於序列化

    將任何類型對應至新的 xsi:type 標記法

    public override bool TryResolveType (Type type, Type declaredType,

        DataContractResolver knownTypeResolver, out XmlDictionaryString typeName,

        out XmlDictionaryString typeNamespace)

    {

        ... // 實作您的對應

    }

}

實作自訂 DataContractResolver 之後,您可以將它提供給 DataContractSerializer,如下所示:

DataContractSerializer dcs = new DataContractSerializer (

    typeof (Object) , null, int.MaxValue, false, true, null,

    new MyDataContractResolver (assembly) ) ;

現在,當您使用此 DataContractSerializer 實例來序列化/還原序列化物件時,將會呼叫您的自訂 DataContractResolver 來執行自訂類型解析。

若要在幕後將自訂 DataContractResolver 插入 WCF 執行時間,您必須撰寫插入 DataContractSerializerOperationBehavior 的 WCF 合約行為,並覆寫預設解析程式。 .NET 4 SDK 隨附完整的範例,說明如何透過合約行為和稱為 [KnownAssembly] 的自訂屬性來完成此作業。

當您想要覆寫 DataContractSerializer 預設值、完全控制用於序列化的類型,或動態管理已知類型時,自訂類型解析很有用。

使用 ReceiveCoNtext 處理佇列訊息

使用 WCF 3.x,您可以在使用 NetMsmqBinding 時,藉由使用交易佇列,並在 MSMQ 交易中登記 WCF 服務作業時,保證訊息的確切一次傳遞。 處理訊息時擲回例外狀況時,WCF 會根據組態) ,將訊息傳回佇列,以確保訊息不會遺失, (可能會傳回原始佇列、有害訊息佇列或寄不出的信件佇列。

雖然這項功能很重要,但人們通常會遇到一些問題。 其中一個是交易成本很高,而且會產生大量讀取/寫入佇列,這會產生更多額外負荷和複雜度。 另一個問題是,無法確保相同的服務下次從佇列提取訊息時處理訊息 (例如,特定服務無法「鎖定」訊息) ,這是您可能想要在特定案例中進行的保證。

為了解決這些問題,WCF 4 引進名為 ReceiveCoNtext 的新 API 來處理已排入佇列的訊息。 使用 ReceiveCoNtext,服務可以在佇列上「查看」訊息以開始處理它,如果發生任何錯誤且擲回例外狀況,則會保留在佇列上。 服務也可「鎖定」訊息以便稍後再重試處理。 ReceiveCoNtext 提供一種機制,可在處理訊息後「完成」訊息,以便從佇列中移除。

這種方法可簡化數個前端的工作,因為訊息不會再透過網路讀取和重新寫入佇列,而且個別訊息不會在處理期間跨不同的服務實例跳動。 讓我們看看一個簡單的範例,以說明其運作方式。

下列範例示範如何使用 ReceiveCoNtext 來處理抵達佇列的訊息:

...

[OperationBehavior (TransactionScopeRequired = true, TransactionAutoComplete = true) ]

[ReceiveCoNtextEnabled (ManualControl = true) ]

public void CalculateProduct (int firstNumber, int secondNumber)

{

    ReceiveCoNtext receiveCoNtext;

    如果 (!ReceiveCoNtext.TryGet (OperationCoNtext.Current.IncomingMessageProperties、

         out receiveCoNtext) )

    {

        Console.WriteLine (「此機器上未安裝/找不到 ReceiveCoNtext」。) ;

        return;

    }

    if ( (firstNumber * secondNumber) % 2 == (receiveCount % 2) )

    {

        receiveCoNtext.Complete (TimeSpan.MaxValue) ;

        Console.WriteLine (「 {0} x {1} = {2} 」, firstNumber, secondNumber,

            firstNumber * secondNumber) ;

    }

    else

    {

        receiveCoNtext.Abandon (TimeSpan.MaxValue) ;

        Console.WriteLine (「 {0}&{1} , firstNumber, secondNumber) ;

    }

    receiveCount++;

}

...

假設成功通過,我們會呼叫 ReceiveCoNtext.Complete 以完成處理導致從基礎佇列中移除訊息的訊息。 否則,我們會呼叫 Abandon,這會在佇列上留下訊息以供日後重試。 .NET 4 SDK 隨附完整的範例,可讓您更深入地探索這項新功能。

使用 ByteStreamMessageEncodingBindingElement 簡化編碼

在某些傳訊案例中,您只需要傳輸二進位資料的「Blob」,而不需要任何包裝或其他處理。 為了簡化此案例,WCF 4 隨附執行此動作的新 ByteStreamMessageEncodingBindingElement。 可惜的是,.NET 4 未隨附此編碼器的新系結,因此您必須撰寫自訂系結,才能使用它。

.NET 4 SDK 隨附完整的範例,說明如何透過自訂系結類別定義使用 ByteStreamMessageEncodingBindingElement 的自訂系結。 當您準備好新的系結類別之後,位元組串流編碼器會變得很容易與您的服務搭配使用。

高效能 ETW 型追蹤

WCF 4 也會改善追蹤和診斷前端。 WCF 4 現在會使用 ETW 進行追蹤,大幅改善追蹤效能,並提供更好的與 Windows Workflow Foundation、Windows Server AppFabric 等相關技術整合,以及 Windows Server 中找到的各種管理技術,為平臺提供更好的模型。

使用 ETW 時,事件提供者可以將事件寫入會話,而會話可以選擇性地將這些事件保存到記錄檔。 取用者可以接聽即時會話事件,或在事實之後從記錄中讀取這些事件。 ETW 控制器負責啟用/停用會話,以及建立提供者與會話的關聯。

.NET 4 SDK 提供簡單的範例,說明如何將這個新的高效能追蹤架構與您的 WCF 服務搭配使用。

結論

WCF 4 帶來許多改善,以及數個全新的功能,可解決現今最常見的通訊案例。 首先,WCF 4 可透過簡化的組態模型更容易使用,並更能支援常見的預設值。

此外,WCF 4 提供服務探索和路由的第一級支援,這些是大部分企業環境和大型 SOA 計畫中的常見需求,這些功能會單獨設定 WCF 與許多競爭架構。 WCF 4 也會簡化 REST 型服務開發,並提供數個其他更進階的 WCF 功能,以解決現今某些特定難題。

除了上述所有專案之外,WCF 4 還提供與 WF 的複雜整合,以提供新的模型來開發宣告式工作流程服務。 工作流程服務可讓您開發長時間執行的非同步服務,以受益于 WF 程式設計模型和基礎執行時間。 而且,由於 Visual Studio 2010 內有新的 WCF 型活動和設計工具支援,這個新的程式設計模型已成為撰寫服務的第一級選項。

在 .NET 4 中,WCF 和 WF 的世界會合並在一起,以提供一致的程式設計模型,為您提供最佳的兩個世界必須提供。 如需 WF 4 新功能的詳細資訊,請參閱 .NET 4 中的 Windows Workflow Foundation 開發人員簡介。

關於作者

Aaron Skonnard 是 Pluralsight 的共同建立者,Microsoft 訓練提供者提供講師導向和隨選 .NET 開發人員課程。 在這幾天,Aaron 會花大部分時間錄製 Pluralsight 隨選! 著重于雲端運算、Windows Azure、WCF 和 REST 的課程。 Aaron 花了數年撰寫、說話及教導世界各地的專業開發人員。 您可以在 和 http://twitter.com/skonnard 連絡 http://pluralsight.com/aaron 他。

其他資源