程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> 基礎內容-通過發現查找新WCF

基礎內容-通過發現查找新WCF

編輯:關於.NET

Microsoft .NET Framework 3.5 對 Windows Communication Foundation (WCF) 的全部 可能調用均具有兩個限制。第一個限制是,分配給服務的端口或管道必須可用。因此,應用 程序開發人員或管理員必須試想或者提供某種方法,以保留這些端口或管道。第二個限制是 ,客戶端必須提前知道服務端點地址,包括端口號和服務器或管道名稱。

最理想的情況是,服務能夠使用任何可用地址。反過來,客戶端就需要在運行時發現此地 址。事實上,存在一種基於行業標准的解決方案,用於規定此發現的定位方式。本專欄的主 題就是該解決方案(簡稱為“發現”)及其支持機制。同時,我還會介紹幾個有用的工具和 幫助程序類。這些內容的源代碼,可在 code.msdn 中找到。

地址發現

發現依賴於用戶數據報協議 (UDP)。與傳輸控制協議 (TCP) 不同,UDP 是無連接協議, 在數據包發送者和接收者之間不需要建立直接連接。客戶端使用 UDP 傳播對任何支持指定約 定類型的端點的發現請求。服務所支持的專門發現端點將接收這些請求。發現端點的實現將 響應客戶端,以提供支持指定約定的服務端點的地址。客戶端發現服務後將對其進行調用, 與常規 WCF 調用相同。有關該過程,在圖 1 中進行了演示。

圖 1 通過 UDP 的發現地址

與元數據交換 (MEX) 端點非常類似,WCF 提供了類型為 UdpDiscoveryEndpoint 的標准 發現端點:

public class DiscoveryEndpoint : ServiceEndpoint

{...}

public class UdpDiscoveryEndpoint : DiscoveryEndpoint

{...}

通過在服務支持的行為集合中添加 ServiceDiscoveryBehavior,可以使主機實現該端點 。可以通過以下編程方式實現此目的:

ServiceHost host = new ServiceHost(...);
host.AddServiceEndpoint(new UdpDiscoveryEndpoint());
ServiceDiscoveryBehavior discovery = new ServiceDiscoveryBehavior();
host.Description.Behaviors.Add(discovery);
host.Open();

圖 2 顯示如何使用服務配置文件添加發現端點和發現行為。

圖 2 在配置文件中添加發現端點

<services>
   <service name = "MyService">
    <endpoint
      kind = "udpDiscoveryEndpoint"
    />
    ...
   </service>
</services>
<behaviors>
   <serviceBehaviors>
    <behavior>
      <serviceDiscovery/>
    </behavior>
   </serviceBehaviors>
</behaviors>

動態地址

發現與服務主機定義其端點的准確度無關。但是,當客戶端希望通過發現查找服務地址時 ,應該如何做?在這種情況下,服務可以基於任何可用端口或管道對其端點地址進行自由動 態配置。

為了使使用動態地址自動化,我編寫了 DiscoveryHelper 靜態幫助程序類,其中包含兩 個屬性:AvailableIpcBaseAddress 和 AvailableTcpBaseAddress:

public static class DiscoveryHelper
{
   public static Uri AvailableIpcBaseAddress
   {get;}
   public static Uri AvailableTcpBaseAddress
   {get;}
}

實現 AvailableIpcBaseAddress 很簡單,因為任何唯一命名的管道都可以實現,該屬性 使用新的全局唯一標識符 (GUID) 命名管道。對於實現 AvailableTcpBaseAddress,需要通 過打開端口 0 查找可用 TCP 端口。

圖 3 顯示如何使用 AvailableTcpBaseAddress。

圖 3 使用動態地址

Uri baseAddress = DiscoveryHelper.AvailableTcpBaseAddress;
ServiceHost host = new ServiceHost(typeof(MyService),baseAddress);
host.AddDefaultEndpoints();
host.Open();
<service name = "MyService">
   <endpoint
    kind = "udpDiscoveryEndpoint"
   />
</service>
<serviceBehaviors>
   <behavior>
    <serviceDiscovery/>
   </behavior>
</serviceBehaviors>

如果您只想獲得服務的動態基址,則僅憑圖 3 中的代碼還不能實現此目的,因為還需要 在配置文件中或以編程方式添加發現。可以使用 EnableDiscovery 主機擴展簡化這些步驟, 定義如下:

public static class DiscoveryHelper
{
   public static void EnableDiscovery(this ServiceHost host,bool  enableMEX = true);
}

在使用 EnableDiscovery 時,不需要編程步驟或配置文件:

Uri baseAddress = DiscoveryHelper.AvailableTcpBaseAddress;
ServiceHost host = new ServiceHost(typeof(MyService),baseAddress);
host.EnableDiscovery();
host.Open();

如果主機尚未為服務定義端點,EnableDiscovery 將添加默認端點。此外, EnableDiscovery 默認向服務的基址上添加 MEX 端點。

客戶端步驟

客戶端使用 DiscoveryClient 類發現支持指定約定的所有服務的全部端點地址:

public sealed class DiscoveryClient : ICommunicationObject
{
   public DiscoveryClient();
   public DiscoveryClient(string endpointName);
   public DiscoveryClient(DiscoveryEndpoint discoveryEndpoint);
   public FindResponse Find(FindCriteria criteria);
   //More members
}

從邏輯上講,DiscoveryClient 是發現端點的代理。與所有代理類似,客戶端必須向代理 的構造函數提供有關目標端點的信息。為此,客戶端可以使用配置文件指定端點或以編程的 方式提供標准 UDP 發現端點,因為不需要地址或綁定等更多詳細信息。然後,客戶端調用 Find 方法,並通過 FindCriteria 實例為其提供發現的約定類型:

public class FindCriteria

{

   public FindCriteria(Type contractType);
   //More members

}

Find 返回一個 FindResponse 實例,其中包含所有已發現端點的集合:

public class FindResponse

{
   public Collection<EndpointDiscoveryMetadata> Endpoints
   {get;}
   //More members
}

每個端點都由 EndpointDiscoveryMetadata 類表示:

public class EndpointDiscoveryMetadata
{
   public EndpointAddress Address
  {get;set;}
   //More members
}

EndpointDiscoveryMetadata 的主要屬性是地址,該屬性最終將包含發現的端點地址。圖 4 顯示了客戶端如何結合使用這些類型以發現端點地址並調用服務。

圖 4 發現和調用端點

DiscoveryClient discoveryClient =
   new DiscoveryClient(new UdpDiscoveryEndpoint());
FindCriteria criteria = new FindCriteria(typeof(IMyContract));
FindResponse discovered = discoveryClient.Find(criteria);
discoveryClient.Close();
//Just grab the first found 
EndpointAddress address = discovered.Endpoints[0].Address;
Binding binding = new NetTcpBinding();
IMyContract proxy =
   ChannelFactory<IMyContract>.CreateChannel(binding,address);
proxy.MyMethod();
(proxy as ICommunicationObject).Close();

圖 4 中有幾個值得注意的問題。

客戶端可能發現多個支持所需約定的端點,但它不具有邏輯分析功能以確定應該調用哪個 端點。只調用返回的集合中的第一個端點。

發現僅用於地址發現。其中未提供有關使用哪個綁定以調用服務的信息。圖 4 只是對 TCP 綁定的使用進行了硬編碼。每當客戶端需要發現服務地址時,不得不反復重復這些極小 的步驟。

發現需要花費時間。默認情況下,Find 方法將等待 20 秒以獲取響應 UDP 發現請求的服 務。這樣的延遲使得發現不適用於許多應用程序,特別是應用程序執行大量緊密調用時。您 可以縮短該超時,但如果這麼做,可能會無法發現部分或所有服務。DiscoveryClient 不提 供異步發現,但異步發現不適用於需要調用服務後才可以繼續執行的客戶端。

在本專欄的稍後部分提供了多個解決這些問題的方法。

作用域

使用發現可能會導致客戶端與服務或其發現的服務之間的關系有些松散。這又出現另一系 列問題 — 客戶端如何可以知道發現了正確的端點?當發現多個兼容端點時,客戶端應該調 用哪一個?

毫無疑問,此時就需要某個機制來幫助客戶端篩選發現結果。這正是作用域的作用所在。 作用域只是與端點關聯的有效 URL。服務可將一個作用域、或者甚至多個作用域與它的每個 端點關聯。作用域與響應發現請求的地址綁定在一起。這樣,客戶端可以基於找到的作用域 篩選發現的地址,甚至還可以在開始時就只嘗試查找相關的作用域。

作用域非常適用於自定義發現以及向應用程序添加復雜行為,尤其適用於編寫框架或管理 工具。作用域的典型用法,就是使客戶端可以區分來自不同應用程序的不同形式的服務。但 是,這種情況有些少見。我發現作用域很適合區分同一應用程序中的端點類型。

例如,假設對於某個給定約定,您具有多個實現。您在生產環境中使用操作模式,在測試 或診斷環境中使用模擬模式。使用作用域,客戶端可以挑選所需的正確實現,並且不同的客 戶端不會因為使用他人的服務而發生沖突。還可以使同一客戶端根據調用上下文選擇不同的 端點。可對端點進行分析、調試、診斷、測試、檢測等等。

主機使用 EndpointDiscoveryBehavior 類為每個端點分配作用域。例如,要為所有端點 都應用作用域,請使用以下默認端點行為:

<endpointBehaviors>
   <behavior>
    <endpointDiscovery>
      <scopes>
       <add scope = "net.tcp://MyApplication"/>
      </scopes>
    </endpointDiscovery>
   </behavior>
</endpointBehaviors>

根據服務類型,通過為每個端點顯式分配行為,對它們分別應用作用域,如圖 5 中所示 。

圖 5 顯式行為分配

<service name = "MySimulator">
   <endpoint behaviorConfiguration = "SimulationScope"
    ...
   />
   ...
</service>
...
   <behavior name = "SimulationScope">
    <endpointDiscovery>
      <scopes>
       <add scope = "net.tcp://Simulation"/>
      </scopes>
    </endpointDiscovery>
   </behavior>

一個發現行為可列出多個作用域:

<endpointDiscovery>
   <scopes>
    <add scope = "net.tcp://MyScope1"/>
    <add scope = "net.tcp://MyScope2"/>
   </scopes>
</endpointDiscovery>

如果端點具有多個關聯的作用域,當客戶端嘗試基於作用域匹配發現該端點時,至少需要 其中一個匹配的作用域,而不是所有。

存在兩種客戶端使用作用域的方法。第一種是將作用域添加到查找條件中:

public class FindCriteria
{
   public Collection<Uri> Scopes
  {get;}
   //More members
}

現在,Find 方法將僅返回兼容端點,其中也列出該作用域。如果客戶端添加多個作用域 ,Find 將僅返回支持所有列出作用域的端點。請注意,端點可能支持未提供給 Find 的其他 作用域。

第二種是用於檢查在 FindResponse 中返回的作用域:

public class EndpointDiscoveryMetadata
{
   public Collection<Uri> Scopes
   {get;}
  //More members
}

這些作用域是端點支持的所有作用域,用於附加篩選。

發現基數

每當使用發現時,客戶端必須處理我所命名的“發現基數”,即發現的端點數及要調用哪 一個(如果存在)。存在下列幾種基數情況:

未發現端點。在此情況下,客戶端需要處理不存在服務的問題。這與其服務不可用的任何 其他 WCF 客戶端無異。

只發現一個兼容端點。目前為止這是最常見的情況 — 客戶端只需繼續調用此服務即可。

發現多個端點。此時,從理論上講,客戶端有兩個選擇。第一個選擇是調用全部端點。這 種情況適用於發布者引發有關訂閱者的事件(相關內容稍後討論),是一個有效的方案。第 二個選擇是調用一些(包括僅調用一個),而不是全部發現的端點。我發現這種方案豪無意 義。每次嘗試為客戶端實現邏輯以解析要調用哪個端點時,在系統上都將創建太多耦合。這 否定了運行時發現的觀點,即任何發現的端點都將被調用。如果可能發現不需要的端點,則 使用發現是一個蹩腳的設計選擇,並且您還需要向客戶端提供靜態地址。

如果客戶端希望只發現一個端點(一個基數),則客戶端應該指示 Find 在發現端點後立 即返回。這樣做會大大縮短發現延遲,從而使 Find 適用於大多數情況。

客戶端可以使用 FindCriteria 的 MaxResults 屬性配置基數:

public class FindCriteria
{
   public int MaxResults
   {get;set;}
   //More members
}
FindCriteria criteria = new FindCriteria(typeof(IMyContract));
criteria.MaxResults = 1;

對於一個基數的情況,可以使用我的 DiscoveryHelper.DiscoverAddress<T> 幫助程序方法簡化:

public static class DiscoveryHelper 
{
  public  static EndpointAddress DiscoverAddress<T>(Uri scope = null);
   //More members 
}

使用 DiscoverAddress<T>,圖 4 將簡化 為:

EndpointAddress address =  DiscoveryHelper.DiscoverAddress<IMyContract>();
Binding binding =  new NetTcpBinding();
IMyContract proxy =  ChannelFactory<IMyContract>.CreateChannel(binding,address);
proxy.MyMethod();
(proxy as ICommunicationObject).Close();

簡化發現

目前為止,客戶端必須對要使用的綁定進行硬編碼。但是,如果服務支持 MEX 端點,則客戶端可以發現 MEX 端點地址,然後繼續檢索並處理元數據以獲取要使用的綁 定以及端點地址。為了幫助實現 MEX 端點發現,FindCriteria 類提供了靜態方法 CreateMetadataExchangeEndpointCriteria:

public class FindCriteria  
{
  public static FindCriteria  CreateMetadataExchangeEndpointCriteria();
//More members 
}

要簡化此過程,請使用我的 DiscoveryFactory.CreateChannel<T> 方 法:

public static class DiscoveryFactory 
{
  public  static T CreateChannel<T>(Uri scope = null);
  //More  members 
}

使用 CreateChannel<T>,圖 4 將簡化為:

IMyContract proxy =  DiscoveryFactory.CreateChannel<IMyContract>();
proxy.MyMethod();
(proxy as ICommunicationObject).Close();

CreateChannel<T> 假定 MEX 端點具有一個基數(即,在本地網絡中僅找到一個可 發現的 MEX 端點),並且假定元數據僅包含一個其約定是指定類型參數 T 的端點。

請注意:CreateChannel<T> 為端點綁定和地址均使用 MEX 端點。服務應該均支持 MEX 端點和發現端點,盡管客戶端從不使用發現端點查找實際端點。

如果存在多個支持所需服務約定的服務,或存在多個 MEX 端點,DiscoveryFactory 也會 提供 CreateChannels<T> 方法:

public static class DiscoveryHelper
{
   public static T[] CreateChannels<T>(bool inferBinding =  true);
   //More members
}

默認情況下,CreateChannels<T> 會根據服務端點的架構推斷出要使用的綁定。如 果 inferBinding 失敗,它將從 MEX 端點發現綁定。

對於兼容的服務端點或 MEX 端點,CreateChannels<T> 不假定一個基數,將返回 包含所有兼容的端點的數組。

通知

從服務的角度來看,迄今提供的發現機制都具有被動性。客戶端查詢發現端點,然後服務 進行響應。作為此被動地址發現的替代方法,WCF 提供了一個活動模型,其中服務將其狀態 傳遞給所有客戶端並提供其地址。當打開服務主機時,它將廣播“您好”通知;當正常關閉 該主機時,將廣播“再見”通知。如果意外中斷該主機,則不會發送“再見”通知。這些通 知在客戶端承載的特殊通知端點上接收(請參見圖 6)。

圖 6 通知體系結構

通知是獨立的端點級別機制,而不是主機級別機制。主機可以選擇要通知哪個端點。每個 通知都包含端點地址、其作用域及其約定。

請注意:通知與地址發現沒有關系。主機可能根本就不支持發現端點,所以不需要發現行 為。或者,主機可能選擇支持發現端點並通知其端點,如圖 6 中所示。

主機可以自動通知其端點。您只需要為發現行為提供有關客戶端通知端點的信息。例如, 使用配置文件時:

<behavior>
   <serviceDiscovery>
    <announcementEndpoints>
      <endpoint
       kind = "udpAnnouncementEndpoint"
      />
    </announcementEndpoints>
   </serviceDiscovery>
</behavior>

My EnableDiscovery 擴展方法也將通知端點添加到發現行為。

為便於客戶端使用,WCF 使用 AnnouncementService 類提供了通知端點的預存儲實現, 如圖 7 中所示。

圖 7 WCF 實現通知端點

public class AnnouncementEventArgs : EventArgs
{
   public EndpointDiscoveryMetadata EndpointDiscoveryMetadata
   {get;}
   //More members
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single,
          ConcurrencyMode = ConcurrencyMode.Multiple)]
public class AnnouncementService : ...
{
   public event EventHandler<AnnouncementEventArgs>  OfflineAnnouncementReceived;
   public event EventHandler<AnnouncementEventArgs>  OnlineAnnouncementReceived;
   //More members
}

AnnouncementService 是針對並發訪問配置的單一實例。AnnouncementService 提供了兩 個客戶端可以訂閱的事件委托,以便客戶端接收通知。客戶端應使用 ServiceHost 的構造函 數托管 AnnouncementService,該構造函數可以接受單個實例。這是必要的,以便客戶端能 夠與實例交互並訂閱事件。此外,客戶端必須將 UDP 通知端點添加到主機:

AnnouncementService announcementService = new AnnouncementService ();
announcementService.OnlineAnnouncementReceived += OnHello;
announcementService.OfflineAnnouncementReceived += OnBye;
ServiceHost host = new ServiceHost(announcementService);
host.AddServiceEndpoint(new UdpAnnouncementEndpoint());
host.Open();
void OnHello(object sender,AnnouncementEventArgs args)
{...}
void OnBye(object sender,AnnouncementEventArgs args)
{...}

此重要細節與接收通知有關。客戶端將接收 Intranet 中的所有服務的全部通知,而不考 慮服務的約定類型或應用程序或作用域。客戶端必須篩選出相關通知。

簡化通知

對於客戶端使用通知所需的原始步驟,可通過我的 AnnouncementSink<T> 類大大 簡化並改進,該類定義如下:

public class AnnouncementSink<T> : AddressesContainer<T>  where T: class
{
   public event Action<T> OnHelloEvent;
   public event Action<T> OnByeEvent;
}

AnnouncementSink<T> 通過封裝圖 7 的步驟使托管通知端點自動化。 AnnouncementSink<T> 在內部托管了一個 AnnouncementService 實例的同時還改進了 自身的不足之處。首先,AnnouncementSink<T> 提供了兩個通知事件委托。與原始 AnnouncementService 不同,AnnouncementSink<T> 可同時引發這些委托。此外, AnnouncementSink<T> 禁用了 AnnouncementService 的同步上下文關聯,所以它可接 受任何傳入線程上的通知,真正實現並發。

AnnouncementSink<T> 篩選約定類型,並僅當兼容端點通知自身時引發 AnnouncementSink<T> 事件。客戶端只需打開和關閉 AnnouncementSink<T>, 就可指示何時開始和停止接收通知。

AnnouncementSink<T> 派生自我的通用地址容器,名為 AddressesContainer<T>。

AddressesContainer<T> 是一個豐富的地址管理幫助程序集合,可用於操作多個地 址。AddressesContainer<T> 支持多個迭代器、索引器、轉換方法和查詢。

圖 8 演示使用 AnnouncementSink<T>。

圖 8 使用 AnnouncementSink<T>

class MyClient : IDisposable
{
   AnnouncementSink<IMyContract> m_AnnouncementSink;
   public MyClient()
   {
    m_AnnouncementSink = new AnnouncementSink<IMyContract>();
    m_AnnouncementSink.OnHelloEvent += OnHello;
    m_AnnouncementSink.Open();
   }
   void Dispose()
   {
    m_AnnouncementSink.Close();
   }
   void OnHello(string address)
   {
    EndpointAddress endpointAddress = new EndpointAddress(address);
   IMyContract proxy = ChannelFactory<IMyContract>.CreateChannel (
    new NetTcpBinding(),endpointAddress);
    proxy.MyMethod();
    (proxy as ICommunicationObject).Close();
   }
}

MEX Explorer

在我編寫的《Programming WCF Services Second Edition》(O’Reilly,2008)一書中 介紹了一個工具,我將其稱之為 MEX Explorer(請參見圖 9)。可以將 MEX 地址提供給 MEX Explorer,並使用 MEX Explorer 反射服務端點(其地址、綁定屬性和約定)。通過采 用發現,我可以修改 MEX Explorer。

圖 9 MEX Explorer

通過單擊“Discover”(發現)按鈕,可觸發對所有 MEX 端點的發現請求,而沒有基數 限制。然後,此工具以樹形顯示所有已發現端點。此外,MEX Explorer 還可利用 MEX 端點 的通知。為響應通知,MEX Explorer 刷新自身並顯示新的端點或從樹中刪除不再運行的端點 。

發現驅動的發布-訂閱模式

在 2006 年 10 月的文章《單向調用、回調以及事件須知》中,我介紹了一種自己開發的 框架,用於支持 WCF 中的發布-訂閱模式。此外,您可以使用發現和通知機制再提供另一種 方法來實現發布-訂閱系統。

與該文章中的技術不同,基於發現的解決方案是唯一的發布-訂閱案例,無需訂閱者或管 理員執行顯式步驟。在使用發現時,無需在代碼或配置文件中進行顯式訂閱。因此,大大簡 化了系統部署,並在顯示發布者和訂閱者時提供很大的靈活性。可以輕松地添加或刪除訂閱 者和發布者,而不需執行其他管理步驟或編程。

當在發布-訂閱系統中使用發現時,訂閱者可以提供發現端點,以便由發布-訂閱服務發現 ;或者可以通知他們的事件處理端點;甚至執行上述兩個操作。

發布者不應直接發現訂閱者,因為可能導致在每次引發事件(基數為所有端點)時產生發 現延遲。而是應該發現發布-訂閱服務,該操作為一次性操作且幾乎沒有成本消耗。發布-訂 閱服務應是單一實例(可啟用快速發現,因為它的基數為一)。發布-訂閱服務公開與訂閱者 相同的事件端點,所以對於發布者該服務貌似元訂閱者。也就是說,在發布-訂閱服務中,需 要使用與實際訂閱者相同的代碼引發事件。

發布-訂閱服務的事件端點必須使用特殊作用域。通過此作用域,發布者可以發現發布-訂 閱服務而不是訂閱者。除支持發現設置作用域的事件端點之外,發布-訂閱服務還提供通知端 點。

發布-訂閱服務維護所有訂閱者的列表。發布-訂閱服務使用某種持續的後台活動,不斷嘗 試發現訂閱者,從而能夠不斷更新訂閱者列表。請再次注意:使發布-訂閱服務的事件端點與 特殊作用域關聯,也可防止該服務在發現所有事件端點時發現自身。發布-訂閱服務還可以提 供通知端點,用於監視訂閱者。圖 10 描述了此體系結構。

圖 10 發現-驅動的發布-訂閱系統

發布-訂閱服務

為促進您自己的發布-訂閱服務部署,我編寫了 DiscoveryPublishService<T>,其 定義如下:

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class DiscoveryPublishService<T> : IDisposable where T:  class
{
   public static readonly Uri Scope;
   protected void FireEvent(params object[] args);
   //More members
}

您只需從 DiscoveryPublishService<T> 派生出發布-訂閱服務,並將事件約定指 定為類型參數。然後,通過調用 FireEvent 方法實現事件約定的操作。

例如,請考慮以下事件約定:

[ServiceContract]
interface IMyEvents
{
   [OperationContract(IsOneWay = true)]
   void OnEvent1();
   [OperationContract(IsOneWay = true)]
   void OnEvent2(int number);
}

圖 11 顯示如何使用 DiscoveryPublishService<T> 實現發布-訂閱服務。

圖 11 實現發布-訂閱服務

class MyPublishService :  DiscoveryPublishService<IMyEvents>,IMyEvents
{
   public void OnEvent1()
   {
    FireEvent();
   }
   public void OnEvent2(int number)
   {
    FireEvent(number);
   }
}

在內部,DiscoveryPublishService<T> 使用我的另一個 AddressContainer<T> 派生類,名為 DiscoveredServices<T>,其定義如下:

public class DiscoveredServices<T> :  AddressesContainer<T> where T: class
{
   public DiscoveredServices();
   public void Abort();
}

DiscoveredServices<T> 旨在保留所有已發現服務的盡可能最新的列表,並將其發 現的地址存儲在其基類中。DiscoveredServices<T> 在後台線程上分拆出一個持續發 現,用於查找已發現地址的當前存儲庫。

FireEvent 方法從消息標頭提取操作名稱。然後,該方法查詢所有不支持發布-訂閱作用 域的訂閱者的列表,從而避免自身發現。之後,FireEvent 將這些列表合並到唯一條目的集 合,這是處理既可通知自身又可發現的訂閱者所需的。對於每個訂閱者,FireEvent 都會從 地址方案推斷出綁定並創建在遇到訂閱者時引發的代理。使用線程池的線程可並發發布這些 事件。

要托管發布-訂閱服務,請使用 DiscoveryPublishService<T> 的靜態幫助程序方 法 CreateHost<S>:

public class DiscoveryPublishService<T> : IDisposable where  T: class
{
   public static ServiceHost<S> CreateHost<S>()
    where S : DiscoveryPublishService<T>,T;
   //More members
}

類型參數 S 是 DiscoveryPublishService<T> 的子類,T 是事件約定。 CreateHost<S> 返回您需要打開的服務主機的實例:

ServiceHost host = DiscoveryPublishService<IMyEvents>.
  CreateHost<MyPublishService>();
host.Open();

此外, CreateHost<S> 還將獲取一個可用的 TCP 基址並添加事件端點,所以無需 配置文件。

發布者

發布者需要事件服務代理。為此,請使用我的 DiscoveryPublishService<T>.CreateChannel:

public class DiscoveryPublishService<T> : IDisposable where  T : class
{
   public static T CreateChannel();
   //More members
}

DiscoveryPublishService<T>.CreateChannel 發現發布-訂閱服務並創建該服務的 代理。該發現速度很快,因為基數是一。發布者的代碼非常簡單:

IMyEvents proxy =  DiscoveryPublishService<IMyEvents>.CreateChannel();
proxy.OnEvent1();
(proxy as ICommunicationObject).Close();

當實現訂閱者後,就不需要特殊操作了。只需支持有關服務的事件約定,並添加事件端點 的發現或通知,或添加兩者。

下載代碼示例: http://code.msdn.microsoft.com/mag2010WCF

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