WCF系列:Binding模型 綁定元素(Binding Element)

在上面的內容中,先後介紹了信道、信道管理器、信道監聽器和信道工廠。從對象的創建來講,信道管理器是信道的創建者。說的再具體點,客戶端的信道通過信道工廠創建,服務端的信道通過信道監聽器創建。但是信道工廠和信道監聽器又是如果被創建出來的呢?

我們在一開始就已經說過,作爲終結點三要素的綁定對象實現了所有的通信細節,並且通過創建信道棧實現了消息的傳遞。從這一點來說,綁定對象無疑是信道層所有通信對象的最終締造者,所以信道工廠和信道監聽器最終的創建都是靠綁定對象實現的。關於這個創建過程又和另一個重要的對象密切相關,那就是綁定元素。

1. 綁定元素(Binding Element)

綁定元素,顧名思義就是構成一個綁定對象的元素。綁定對象最根本的目的就是創建信道棧,藉此實現對消息的傳輸、編碼和基於消息交換的其他功能,比如安全、可靠傳輸、事務流轉等等。組成信道棧的單個信道對象基於對某項單一功能的實現,在不同環境中,我們需要根據具體的需要創建相應的信道,並根據一定的順序把這些信道組成一個完整的信道棧。對於綁定對象來說,如何實現這種靈活、自由的信道常創建方式,這得益於基於綁定元素的設計模式

1.1. 關於綁定元素

從結構的角度講,一個綁定對象有一系列綁定元素組成,每個綁定元素負責創建相應的信道。所以綁定元素幾何的構成以及它們之間的先後順序,決定了最終生成的信道棧中的信道組成已經它們位於棧中的先後順序。WCF之所以在設計的時候將綁定和綁定元素分離開發,是基於靈活性、可擴展性考慮的。

在介紹信道和信道棧的時候我們說過,我們不可能、也不應該創建一個萬能的信道能夠提供消息交換中的所有的功能,所以我們讓一個信道只承載某個單一的功能,比如傳輸信道專注於網絡傳輸,消息編碼信到專注於消息的編碼,WCF還定義了一一系列的信道,他們分別關注與安全、可靠傳輸和事務流轉等等。這種信道組合的設計方式使得我們可以根據具體的需求來定製我們將要創建的信道棧,讓它只具有我們必須的功能,而去除不必要的功能。

同理,我們可以根據具體實際需求,將必要的綁定元素進行有序的組合,從而創建最能適合具體場景的綁定對象。由於信道可以分爲必須的傳輸信道、消息編碼信道和可選的基於某種WS-*協議實現的協議信道,與之相對地,我們的綁定元素可以分爲傳輸綁定元素、消息編碼綁定元素和協議綁定元素。

由於信道的實際創建者是信道管理器(信道工廠和信道監聽器),所以綁定元素只需要實現對信道管理器的創建,而最終實現對具體信道的創建。所以綁定元素的最根本的功能就是實現對信道監聽器和信道工廠的創建。這可以從所有綁定元素的基類, System.ServiceModel.Channels.BindingElement的定義上看出來:
  1. public abstract class BindingElement
  2. {
  3.     // Methods
  4.     protected BindingElement();
  5.     protected BindingElement(BindingElement elementToBeCloned);

  6.     public virtual IChannelFactory<TChannel> BuildChannelFactory<TChannel>(BindingContext context);
  7.     public virtual IChannelListener<TChannel> BuildChannelListener<TChannel>(BindingContext context) where TChannel : class, IChannel;
  8.     public virtual bool CanBuildChannelFactory<TChannel>(BindingContext context);
  9.     public virtual bool CanBuildChannelListener<TChannel>(BindingContext context) where TChannel : class, IChannel;
  10.     public abstract BindingElement Clone();
  11.     public abstract T GetProperty<T>(BindingContext context) where T : class;
  12. }
複製代碼
BindingElement的核心方法成員有兩個:BuildChannelListener<TChannel>和BuildChannelFactory<TChannel〉,用於創建相應的信道監聽器和信道工廠。兩個Build方法的參數都是BindingContext,關於BindingContext,我將在本章後續的部分中介紹。而CanBuildChannelFactory<TChannel>和CanBuildChannelListener<TChannel〉 則屬性兩個測試性質的方法,用於檢驗相應的相應的信道監聽器和信道功能是否可以被創建。

1.2. 案例演示:如何自定義綁定元素

接下來,我們通過一個案例來演示如果自定義一個綁定元素。通過該綁定元素來創建我們在上面一個案例中創建的兩個自定義信道管理器:SimpleChannelFactory和SimpleChannelListener。按照上面的命名方式,我們把這個自定義綁定元素命名爲:SimpleBindingElement,下面是整個SimpleBindingElement的定義:
  1. public class SimpleBindingElement : BindingElement
  2. {
  3.     public SimpleBindingElement()
  4.     {
  5.         PrintHelper.Print(this, "SimpleBindingElement");
  6.     }

  7.     public override BindingElement Clone()
  8.     {
  9.         PrintHelper.Print(this, "Clone");
  10.         return new SimpleBindingElement();
  11.     }

  12.     public override T GetProperty<T>(BindingContext context)
  13.     {
  14.         PrintHelper.Print(this, string.Format("GetProperty<{0}>", typeof(T).Name));
  15.         return context.GetInnerProperty<T>();
  16.     }

  17.     public override IChannelFactory<TChannel> BuildChannelFactory<TChannel>(BindingContext context)
  18.     {
  19.         PrintHelper.Print(this, "BuildChannelFactory<TChannel>");
  20.         return new SimpleChannelFactory<TChannel>(context) as IChannelFactory<TChannel>;
  21.     }

  22.     public override IChannelListener<TChannel> BuildChannelListener<TChannel>(BindingContext context)
  23.     {
  24.         PrintHelper.Print(this, "BuildChannelListener<TChannel>");
  25.         return new SimpleChannelListener<TChannel>(context) as IChannelListener<TChannel>;
  26.     }
  27. }
複製代碼
SimpleBindingElement直接繼承自抽象的基類BindingElement,對SimpleChannelFactory和SimpleChannelListener的創建分別實現在兩個被重寫的方法中:BuildChannelFactory<TChannel>和BuildChannelListener<TChannel>中。此外還重寫了兩個額外的方法:Clone和GetProperty<T>,前者用於克隆一個新的綁定元素,後一個和定義在信道、信道管理器的同名方法一樣,用於獲取基於某種類型的屬性。

2. 綁定揭祕

前面我們一直在談論信道、信道管理器、信道監聽器、信道工廠和綁定元素,現在我們才進體本章的主題。不過由於前面的鋪墊已經很足了,綁定本身反而沒有太多可大書特書了。如果從結構上給綁定下個定義,那麼我的定義很簡單:“綁定是綁定元素的有序集合”。

2.1. 綁定是綁定元素的有序集合

由於綁定的終極目標是實現對信道棧的創建,而對於一個信道棧來說,信道的構成和次序決定着該信道棧在最終消息通信中的特性與能力,而綁定元素有決定着信道的創建,所以綁定對象本身的特性與能力由構成它的所有綁定元素以及這些綁定元素之間的先後次序決定。

正因爲如此,當我們需要判斷某一個綁定類型是否支持某種特性的時候,可以通過查看該綁定是否具有與此特性相關的綁定元素。比如,我們要判斷某種綁定是否支持某種類型的傳輸協議,只需要看看構成該綁定的傳輸綁定元素就可以了,WsHttpBinding的傳輸綁定元素是HttpTransportBindingElement,所以 只能支持基於HTTP或者HTTPS的傳輸協議;如果需要確定某種類型的綁定是否支持事務的流轉,只需要查看該綁定的綁定元素集合中是否包含TransactionFlowBindingElement就可以了。

在WCF中,所有的綁定都直接或者間接繼承自抽象基類:System.ServiceModel.Channels.Binding,我們現在來簡單地分析一下這個基類。Binding實現了接口IDefaultCommunicationTimeouts。
  1. public abstract class Binding : IDefaultCommunicationTimeouts
  2. {
  3.     ... ...
  4.    
  5.     public TimeSpan OpenTimeout { get; set; }
  6.     public TimeSpan CloseTimeout { get; set; }   
  7.     public TimeSpan SendTimeout { get; set; }
  8.     public TimeSpan ReceiveTimeout { get; set; }
  9. }
複製代碼
四個默認的超時時限可以通過編程的方式顯式指定,也可以通過配置的方式進行設置。他們的默認值分別爲:OpenTimeout和CloseTimeout爲1分鐘,而SendTimeout和ReceiveTimeout爲10分鐘。

對於Binding,最爲重要的就是如果構建組成該綁定對象的所有綁定元素集合。基於綁定元素的創建,通過抽象方法CreateBindingElements實現,所有具體的綁定類型均需要實現該方法。
  1. public abstract class Binding : IDefaultCommunicationTimeouts

  2.     ... ...
  3.     public abstract BindingElementCollection CreateBindingElements(); 
  4. }
複製代碼
由於信道管理器棧創建相應的信道棧,而綁定創建信道管理器棧,因此在Binding中定義了一系列BuildChannelFactory<TChannel>和BuildChannelListener<TChannel>方法重載,用於創建信道工廠棧和信道監聽器棧。此外,和BindingElement一樣,CanBuildChannelFactory<TChannel>和CanBuildChannelListener<TChannel>用於檢測綁定對象創建信道工廠和信道監聽器的能力:
  1. public abstract class Binding : IDefaultCommunicationTimeouts
  2. {
  3.     ...
  4.     public IChannelFactory<TChannel> BuildChannelFactory<TChannel>(params object[] parameters);
  5.     public virtual IChannelFactory<TChannel> BuildChannelFactory<TChannel>(BindingParameterCollection parameters);
  6.    
  7.     public virtual IChannelListener<TChannel> BuildChannelListener<TChannel>(params object[] parameters) where TChannel : class, IChannel;
  8.     public virtual IChannelListener<TChannel> BuildChannelListener<TChannel>(BindingParameterCollection parameters) where TChannel : class, IChannel;
  9.     public virtual IChannelListener<TChannel> BuildChannelListener<TChannel>(Uri listenUriBaseAddress, params object[] parameters) where TChannel : class, IChannel;
  10.     public virtual IChannelListener<TChannel> BuildChannelListener<TChannel>(Uri listenUriBaseAddress, BindingParameterCollection parameters) where TChannel : class, IChannel;
  11.     public virtual IChannelListener<TChannel> BuildChannelListener<TChannel>(Uri listenUriBaseAddress, string listenUriRelativeAddress, params object[] parameters) where TChannel : class, IChannel;
  12.     public virtual IChannelListener<TChannel> BuildChannelListener<TChannel>(Uri listenUriBaseAddress, string listenUriRelativeAddress, BindingParameterCollection parameters) where TChannel : class, IChannel;
  13.     public virtual IChannelListener<TChannel> BuildChannelListener<TChannel>(Uri listenUriBaseAddress, string listenUriRelativeAddress, ListenUriMode listenUriMode, params object[] parameters) where TChannel : class, IChannel;
  14.     public virtual IChannelListener<TChannel> BuildChannelListener<TChannel>(Uri listenUriBaseAddress, string listenUriRelativeAddress, ListenUriMode listenUriMode, BindingParameterCollection parameters) where TChannel : class, IChannel;
  15.    
  16.     public virtual bool CanBuildChannelFactory<TChannel>(BindingParameterCollection parameters);
  17.     public bool CanBuildChannelFactory<TChannel>(params object[] parameters);
  18.    
  19.     public bool CanBuildChannelListener<TChannel>(params object[] parameters) where TChannel : class, IChannel;
  20.     public virtual bool CanBuildChannelListener<TChannel>(BindingParameterCollection parameters) where TChannel : class, IChannel;
  21. }
複製代碼
2.2. 案例演示:如何創建自定義綁定

在上一個案例演示中,我們創建了自定義的綁定元素:SimpleBindingElement,在案例中我們來真正使用綁定元素,爲此我們創建一個直接繼承自Binding的自定義綁定。爲了簡單起見,對於我們自定義的綁定,他僅僅包含三個必須的綁定元素:傳輸綁定元素和消息編碼綁定元素,外加我們自定義的綁定元素。對於傳輸,我們採用基於HTTP協議的HttpTransportBindingElement,而對應消息編碼,則採用基於文本編碼方式的TextMessageEncodingBindingElement。我們索性將我們自定義的綁定命名爲SimpleBinding,下面是SimpleBinding的定義:
  1. namespace Artech.CustomChannels
  2. {
  3.     public class SimpleBinding : Binding
  4.     {
  5.         private string _scheme;
  6.         private TransportBindingElement _transportBindingElement = new HttpTransportBindingElement();
  7.         private MessageEncodingBindingElement _messageEncodingBindingElement = new TextMessageEncodingBindingElement();
  8.         private SimpleBindingElement _SimpleBindingElement = new SimpleBindingElement();

  9.         public override BindingElementCollection CreateBindingElements()
  10.         {
  11.             BindingElementCollection elemens = new BindingElementCollection();
  12.             elemens.Add(this._SimpleBindingElement);
  13.             elemens.Add(this._messageEncodingBindingElement);
  14.             elemens.Add(this._transportBindingElement);
  15.             return elemens.Clone();
  16.         }

  17.         public override string Scheme
  18.         {
  19.             get
  20.             {
  21.                 return this._transportBindingElement.Scheme;
  22.             }
  23.         }
  24.     }
  25. }
複製代碼
對於整個SimpleBinding的定義,從形式上看顯得異常的簡單,僅僅是實現了定義在Binding中的抽象方法CreateBindingElements。在CreateBindingElements方法中,返回一個表示綁定元素集合的BindingElementCollection對象,在該集合中,包含三種類型的綁定元素,有上到下的順序分別爲:SimpleBindingElement、MessageEncodingBindingElement和HttpTransportBindingElement。此外重寫了Scheme只讀屬性,它返回HttpTransportBindingElement的scheme:http。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章