在上一篇文章中,我們通過一個直接借助BasicHttpBinding對象實現Client和Server端進行通信的例子,對WCF channel layer進行了一個大致上的介紹。由此引出了一些列通信相關的概念和對象,比如Channel,Output channel, Input channel,Request channel, Reply Channel,Duplex channel, Channel Shape,Channel manager,Channel factory, Channel listener, Binding element 等。通過這些元素,我們很容易地實現對WCF channel layer進行擴展。
對channel layer進行擴展一般適用於當你的需求通過現有的Binding,或者channel不能實現,而需要自定義一些channel來實現你所需的功能。不如現在的WCF系統定義的Channel中沒有實現對Message body的壓縮功能。你可以就需要將此功能定義到一個custom channel中,然後將其注入到channel stack中。一般來說,僅僅創建custom channel是不夠的,因為在runtime, channel是通過Channel manager進行創建的,所以你需要創建對應的Channel factory(如何對發送方進行擴展)或者Channel listener(如果對接受方進行擴展)。而Channel factory和channel listener最終又是通過Binding element進行創建的,所以你還需要創建相應的Binding element。(Binding element=〉Channel factory&Channel listener=>Channel)
在本章節中,我們將繼續討論WCF channel layer。我們將通過如何創建和應用custom channel來介紹channel layer一些知識。
1、ICommunicationObject 和 CommunicationObject
我們知道WCF channel layer的絕大部分對象,比如Channel,Channel factory,Channel listener,從功能上講都是用於通信(Communication)的對象,對傳統的communication object,比如socket,他們往往都具有通過狀態和狀態轉化規則(狀態機:State machine)。這些狀態包括Creating、Created、Opening、Opened、Closing、Closed等等。為了統一管理這些狀態和狀態之間的轉化,WCF定義個一個特殊的Interface:ICommunicationObject
public interface ICommunicationObject
{
// Events
event EventHandler Closed;
event EventHandler Closing;
event EventHandler Faulted;
event EventHandler Opened;
event EventHandler Opening;
// Methods
void Abort();
IAsyncResult BeginClose(AsyncCallback callback, object state);
IAsyncResult BeginClose(TimeSpan timeout, AsyncCallback callback, object state);
IAsyncResult BeginOpen(AsyncCallback callback, object state);
IAsyncResult BeginOpen(TimeSpan timeout, AsyncCallback callback, object state);
void Close();
void Close(TimeSpan timeout);
void EndClose(IAsyncResult result);
void EndOpen(IAsyncResult result);
void Open();
void Open(TimeSpan timeout);
// Properties
CommunicationState State { get; }
}
ICommunicationObject定義了3種成員:
Property:State, 得到當前的狀態,返回值是一個CommunicationState 枚舉。
Method:同步、異步Open和Close方法。
Event:通過注冊這些狀態相關的Event,當時對象轉化到對應的狀態時執行相應操作。
WCF定義了一個abstract class: CommunicationObject直接實現了該Interface。CommunicationObject的實現統一的State machine。WCF channel layer的很多的class都直接或者間接的繼承了這個class。你也可以讓你的class繼承該class。當你讓你自己的class繼承CommunicationObject的時候,在override 掉base相應的method的時候,強烈建議你先調用base對應的方法,CommunicationObject會幫你進行相應的State轉換和觸發相應的事件。
2. Channel 和Channel Shape
在上一篇文章中,我們討論過了。在不同的消息交換模式(MEP)中,發送方和接受方的Channel扮演的角色是不相同的。我們並把這種不同MEP中消息交互雙方Channel的結構差異表述為Channel shape。我們有四種不同的Channel shape:Datagram、Request/reply、Duplex和P2P。不同Channel shape中Channel的結構性差性通過實現不同的Channel interface來體現。
對於Datagram channel shape,采用了One-way的MEP。發送方的channel 必須實現IOutputChannel interface。該Interface的方法成員主要集中在用於發送message的Send方法(同步/異步):
public interface IOutputChannel : IChannel, ICommunicationObject
{
// Methods
IAsyncResult BeginSend(Message message, AsyncCallback callback, object state);
IAsyncResult BeginSend(Message message, TimeSpan timeout, AsyncCallback callback, object state);
void EndSend(IAsyncResult result);
void Send(Message message);
void Send(Message message, TimeSpan timeout);
// Properties
EndpointAddress RemoteAddress { get; }
Uri Via { get; }
}
與之相應是IInputChannel inteface,該Interface用於Datagram channel shape中接收方的channel定義。其主要方法成員主要集中在用於接收Message的Receive方法(同步/異步):
public interface IInputChannel : IChannel, ICommunicationObject
{
// Methods
IAsyncResult BeginReceive(AsyncCallback callback, object state);
IAsyncResult BeginReceive(TimeSpan timeout, AsyncCallback callback, object state);
IAsyncResult BeginTryReceive(TimeSpan timeout, AsyncCallback callback, object state);
IAsyncResult BeginWaitForMessage(TimeSpan timeout, AsyncCallback callback, object state);
Message EndReceive(IAsyncResult result);
bool EndTryReceive(IAsyncResult result, out Message message);
bool EndWaitForMessage(IAsyncResult result);
Message Receive();
Message Receive(TimeSpan timeout);
bool TryReceive(TimeSpan timeout, out Message message);
bool WaitForMessage(TimeSpan timeout);
// Properties
EndpointAddress LocalAddress { get; }
}
注:無論對於同步或者異步方法,一般由兩個重載,一個接收一個TimeSpan 作為參數,表是Send或者Receive允許的時間范圍。而另一個沒有該參數的方式,不不是建議你使用一個無限的TimeSpan,而是使用一個可配置的默認時間段(實際上是Binding對象對應的屬性)
不同於Datagram channel shape,Request/request channel shape下交互雙方的Channel具有不同的行為。發送方的Channel實現IRequestChannel。該interface的方面成員主要集中在一些用於向接收方進行請求的Request方法(同步/異步):上面。
public interface IRequestChannel : IChannel, ICommunicationObject
{
// Methods
IAsyncResult BeginRequest(Message message, AsyncCallback callback, object state);
IAsyncResult BeginRequest(Message message, TimeSpan timeout, AsyncCallback callback, object state);
Message EndRequest(IAsyncResult result);
Message Request(Message message);
Message Request(Message message, TimeSpan timeout);
// Properties
EndpointAddress RemoteAddress { get; }
Uri Via { get; }
}
同理,對於接收方的IReplyChannel則主要定義了一些用於Reply的方法:
public interface IReplyChannel : IChannel, ICommunicationObject
{
// Methods
IAsyncResult BeginReceiveRequest(AsyncCallback callback, object state);
IAsyncResult BeginReceiveRequest(TimeSpan timeout, AsyncCallback callback, object state);
IAsyncResult BeginTryReceiveRequest(TimeSpan timeout, AsyncCallback callback, object state);
IAsyncResult BeginWaitForRequest(TimeSpan timeout, AsyncCallback callback, object state);
RequestContext EndReceiveRequest(IAsyncResult result);
bool EndTryReceiveRequest(IAsyncResult result, out RequestContext context);
bool EndWaitForRequest(IAsyncResult result);
RequestContext ReceiveRequest();
RequestContext ReceiveRequest(TimeSpan timeout);
bool TryReceiveRequest(TimeSpan timeout, out RequestContext context);
bool WaitForRequest(TimeSpan timeout);
// Properties
EndpointAddress LocalAddress { get; }
}
而對與Duplex和P2P,消息交互雙方使用相同的Channel:Duplex channel。本質上講,DuplexChannel = OutputChannel + IntputChannel。這一點從IDuplexChannel的定義上就可以看出來:
public interface IDuplexChannel : IInputChannel, IOutputChannel, IChannel, ICommunicationObject
3、創建Custom Channel
{
}
為了讓大家對WCF channel layer有一個深刻的認識,以及掌握如何有效地對其進行擴展。我在整篇文章中穿插介紹一個具體的Sample:創建一個自定義的channel,以及相關的輔助對象,比如Channel factory、Channel listener和Binding element。
這個Sample將基於我們最為常用的Request/Reply channel shape。所以我們需要創建兩個Channel,一個是用於發送方的實現了IRequestChannel的Channel,而另一個則是實現了IReplyChannel的用於接收方的Channel。
為了簡單起見,在我定義的channel的每個方法僅僅打印出相應的方法名稱而已(這樣做不但簡單,還有的一個好處,那就是當我最後將其應用到具體的Messaging場景中,可以根據控制台打印出來的文字清楚地看清當我們的Channel應用到具體的場景中後先後執行了那些方法)。
我們先來看看實現了IRequestChannel的MyRequestChannel的定義:
namespace Artech.ChannleStackExplore.Channels
{
public class MyRequestChannel :ChannelBase, IRequestChannel
{
private IRequestChannel InnerChannel
{get;set;}
public MyRequestChannel(ChannelManagerBase channleManager, IRequestChannel innerChannel)
: base(channleManager)
{
this.InnerChannel = innerChannel;
}
ChannelBase Members#region ChannelBase Members
protected override void OnAbort()
{
Console.WriteLine("MyRequestChannel.OnAbort()");
this.InnerChannel.Abort();
}
protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)
{
Console.WriteLine("MyRequestChannel.OnBeginClose()");
return this.InnerChannel.BeginClose(timeout, callback, state);
}
protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
{
Console.WriteLine("MyRequestChannel.OnBeginOpen()");
return this.InnerChannel.BeginOpen(timeout, callback, state);
}
protected override void OnClose(TimeSpan timeout)
{
Console.WriteLine("MyRequestChannel.OnClose()");
this.Close(timeout);
}
protected override void OnEndClose(IAsyncResult result)
{
Console.WriteLine("MyRequestChannel.OnEndClose()");
this.InnerChannel.EndClose(result);
}
protected override void OnEndOpen(IAsyncResult result)
{
Console.WriteLine("MyRequestChannel.OnEndOpen()");
this.InnerChannel.EndOpen(result);
}
protected override void OnOpen(TimeSpan timeout)
{
Console.WriteLine("MyRequestChannel.OnOpen()");
this.InnerChannel.Open(timeout);
}
#endregion
IRequestChannel Members#region IRequestChannel Members
public IAsyncResult BeginRequest(Message message, TimeSpan timeout, AsyncCallback callback, object state)
{
Console.WriteLine("MyRequestChannel.BeginRequest()");
return this.BeginRequest(message, timeout, callback, state);
}
public IAsyncResult BeginRequest(Message message, AsyncCallback callback, object state)
{
Console.WriteLine("MyRequestChannel.BeginRequest()");
return this.InnerChannel.BeginRequest(message, callback, state);
}
public Message EndRequest(IAsyncResult result)
{
Console.WriteLine("MyRequestChannel.EndRequest()");
return this.InnerChannel.EndRequest(result);
}
public EndpointAddress RemoteAddress
{
get
{
Console.WriteLine("MyRequestChannel.RemoteAddress");
return this.InnerChannel.RemoteAddress;
}
}
public Message Request(Message message, TimeSpan timeout)
{
Console.WriteLine("MyRequestChannel.Request()");
return this.InnerChannel.Request(message, timeout);
}
public Message Request(Message message)
{
Console.WriteLine("MyRequestChannel.Request()");
return this.InnerChannel.Request(message);
}
public Uri Via
{
get
{
Console.WriteLine("MyRequestChannel.Via)");
return this.InnerChannel.Via;
}
}
#endregion
}
}
這裡唯一需要注意的一點是:在實際的運行環境中,我們的channel僅僅了位於Channel stack的某個環節。該channel和其他的一些channel組成一個管道,這個管道裡流淌是Message。所以當一個Channel執行了它相應的操作的時候,需要將message傳到下一個channel作進一步處理。所有我們的Channel需要下一個Channel的應用,這個Channel就是我們的InnerChannel字段,該成員在構造函數中指定。
private IRequestChannel InnerChannel
所以,對於每一個方法,在實現了本Channel的功能之後,只需要調用InnerChannel 的對應的方法即可。
{get;set;}
public MyRequestChannel(ChannelManagerBase channleManager, IRequestChannel innerChannel)
: base(channleManager)
{
this.InnerChannel = innerChannel;
}
我們再來看看實現了IReplyChannel的MyReplyChannel, 它用於接收方:
namespace Artech.ChannleStackExplore.Channels
{
public class MyReplyChannel: ChannelBase, IReplyChannel
{
private IReplyChannel InnerChannel
{ get; set; }
public MyReplyChannel(ChannelManagerBase channelManager, IReplyChannel innerChannel):base(channelManager)
{
this.InnerChannel = innerChannel;
}
ChannelBase Members#region ChannelBase Members
protected override void OnAbort()
{
Console.WriteLine("MyReplyChannel.OnAbort()");
this.InnerChannel.Abort();
}
protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)
{
Console.WriteLine("MyReplyChannel.OnBeginClose()");
return this.InnerChannel.BeginClose(timeout, callback, state);
}
protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
{
Console.WriteLine("MyReplyChannel.OnBeginOpen()");
return this.InnerChannel.BeginOpen(timeout, callback, state);
}
protected override void OnClose(TimeSpan timeout)
{
Console.WriteLine("MyReplyChannel.OnClose()");
this.Close(timeout);
}
protected override void OnEndClose(IAsyncResult result)
{
Console.WriteLine("MyReplyChannel.OnEndClose()");
this.InnerChannel.EndClose(result);
}
protected override void OnEndOpen(IAsyncResult result)
{
Console.WriteLine("MyReplyChannel.OnEndOpen()");
this.InnerChannel.EndOpen(result);
}
protected override void OnOpen(TimeSpan timeout)
{
Console.WriteLine("MyReplyChannel.OnOpen()");
this.InnerChannel.Open(timeout);
}
#endregion
IReplyChannel Members#region IReplyChannel Members
public IAsyncResult BeginReceiveRequest(TimeSpan timeout, AsyncCallback callback, object state)
{
Console.WriteLine("MyReplyChannel.BeginReceiveRequest()");
return this.InnerChannel.BeginReceiveRequest(timeout, callback, state);
}
public IAsyncResult BeginReceiveRequest(AsyncCallback callback, object state)
{
Console.WriteLine("MyReplyChannel.BeginReceiveRequest()");
return this.InnerChannel.BeginReceiveRequest(callback, state);
}
public IAsyncResult BeginTryReceiveRequest(TimeSpan timeout, AsyncCallback callback, object state)
{
Console.WriteLine("MyReplyChannel.BeginTryReceiveRequest()");
return this.InnerChannel.BeginTryReceiveRequest(timeout, callback, state);
}
public IAsyncResult BeginWaitForRequest(TimeSpan timeout, AsyncCallback callback, object state)
{
Console.WriteLine("MyReplyChannel.BeginWaitForRequest()");
return this.InnerChannel.BeginWaitForRequest(timeout, callback, state);
}
public RequestContext EndReceiveRequest(IAsyncResult result)
{
Console.WriteLine("MyReplyChannel.EndReceiveRequest()");
return this.InnerChannel.EndReceiveRequest(result);
}
public bool EndTryReceiveRequest(IAsyncResult result, out RequestContext context)
{
Console.WriteLine("MyReplyChannel.EndTryReceiveRequest()");
return this.InnerChannel.EndTryReceiveRequest(result, out context);
}
public bool EndWaitForRequest(IAsyncResult result)
{
Console.WriteLine("MyReplyChannel.EndWaitForRequest()");
return this.InnerChannel.EndWaitForRequest(result);
}
public System.ServiceModel.EndpointAddress LocalAddress
{
get
{
Console.WriteLine("MyReplyChannel.LocalAddress");
return this.InnerChannel.LocalAddress;
}
}
public RequestContext ReceiveRequest(TimeSpan timeout)
{
Console.WriteLine("MyReplyChannel.ReceiveRequest()");
return this.InnerChannel.ReceiveRequest(timeout);
}
public RequestContext ReceiveRequest()
{
Console.WriteLine("MyReplyChannel.ReceiveRequest()");
return this.InnerChannel.ReceiveRequest();
}
public bool TryReceiveRequest(TimeSpan timeout, out RequestContext context)
{
Console.WriteLine("MyReplyChannel.TryReceiveRequest()");
return this.InnerChannel.TryReceiveRequest(timeout, out context);
}
public bool WaitForRequest(TimeSpan timeout)
{
Console.WriteLine("MyReplyChannel.WaitForRequest()");
return this.InnerChannel.WaitForRequest(timeout);
}
#endregion
}
}
MyReplyChannel的定義方式和MyRequestChannel完全一樣,我們就不用再多說什麼了。
4. 創建Custom Channel Factory & Channel Listener
通過上一篇文章的介紹,我們知道Channel是通過Channel Manager來創建並管理的,在發送方的Channel Manager被稱為Channel Factory。
對於Channel factory,除了定義了兩個Interface之外(IChannelFactory 和IChannelFactory<TChannel>)
public interface IChannelFactory : ICommunicationObject
{
// Methods
T GetProperty<T>() where T : class;
}
public interface IChannelFactory<TChannel> : IChannelFactory, ICommunicationObject
{
// Methods
TChannel CreateChannel(EndpointAddress to);
TChannel CreateChannel(EndpointAddress to, Uri via);
}
還定義了兩個Base class:ChannelFactoryBase 和ChannelFactoryBase<TChannel>(限於篇幅,在這裡就不多作介紹了)。
為了簡單起見,我們上我們的Channel factory繼承自ChannelFactoryBase<TChannel>:
namespace Artech.ChannleStackExplore.Channels
{
public class MyChannelFactory<TChannel> : ChannelFactoryBase<TChannel>
{
private IChannelFactory<TChannel> InnerChannelFactory
{ get; set; }
public MyChannelFactory(BindingContext context)
{
this.InnerChannelFactory = context.BuildInnerChannelFactory<TChannel>();
}
protected override TChannel OnCreateChannel(EndpointAddress address, Uri via)
{
Console.WriteLine("MyChannelFactory<TChannel>.OnClose()");
TChannel innerChannel = this.InnerChannelFactory.CreateChannel(address, via);
return (TChannel)(object)(new MyRequestChannel(this, innerChannel as IRequestChannel));
}
protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
{
Console.WriteLine("MyChannelFactory<TChannel>.OnBeginOpen()");
return this.InnerChannelFactory.BeginOpen(timeout, callback, state);
}
protected override void OnEndOpen(IAsyncResult result)
{
Console.WriteLine("MyChannelFactory<TChannel>.OnEndOpen()");
this.InnerChannelFactory.EndOpen(result);
}
protected override void OnOpen(TimeSpan timeout)
{
Console.WriteLine("MyChannelFactory<TChannel>.OnOpen()");
this.InnerChannelFactory.Open();
}
}
}
我們說過,和Channel stack一樣,Channel factory仍然是一個stack,原因很簡單,一個個的Channel需要相應的channel factory來創建。同Channel一樣,當channel factory創建了自己的channel之後需要將接力棒交到下一個Channel factory。不過不通於Channel的是,下一個Channel factory不時在構造函數直接指定的,而是通過構造函數中的BindingContext 對象的BuildInnerChannelFactory()創建。
private IChannelFactory<TChannel> InnerChannelFactory
{ get; set; }
public MyChannelFactory(BindingContext context)
{
this.InnerChannelFactory = context.BuildInnerChannelFactory<TChannel>();
}
注:BindingContext 的兩個最重要的方法就是BuildInnerChannelFactory和BuildInnerChannelListener。前者創建Inner channel factory後者創建Inner Channel listener。
熟悉了ChannelFactory的定義,大家很自然的想得到ChannelListner的定義(不過ChannelListner的成員比ChannelFactory 要多些):
namespace Artech.ChannleStackExplore.Channels
{
public class MyChannelListener<TChannel> : ChannelListenerBase<TChannel> where TChannel : class, IChannel
{
private IChannelListener<TChannel> InnerChannelListener
{ get; set; }
public MyChannelListener(BindingContext context)
{
this.InnerChannelListener = context.BuildInnerChannelListener<TChannel>();
}
protected override TChannel OnAcceptChannel(TimeSpan timeout)
{
Console.WriteLine("MyChannelListener<TChannel>.OnAcceptChannel()");
TChannel innerChannel = this.InnerChannelListener.AcceptChannel(timeout);
return new MyReplyChannel(this, innerChannel as IReplyChannel) as TChannel;
}
protected override IAsyncResult OnBeginAcceptChannel(TimeSpan timeout, AsyncCallback callback, object state)
{
Console.WriteLine("MyChannelListener<TChannel>.OnBeginAcceptChannel()");
return this.InnerChannelListener.BeginAcceptChannel(timeout, callback, state);
}
protected override TChannel OnEndAcceptChannel(IAsyncResult result)
{
Console.WriteLine("MyChannelListener<TChannel>.OnEndAcceptChannel()");
TChannel innerChannel = this.InnerChannelListener.EndAcceptChannel(result);
return new MyReplyChannel(this, innerChannel as IReplyChannel) as TChannel;
}
protected override IAsyncResult OnBeginWaitForChannel(TimeSpan timeout, AsyncCallback callback, object state)
{
Console.WriteLine("MyChannelListener<TChannel>.OnBeginWaitForChannel()");
return this.InnerChannelListener.BeginWaitForChannel(timeout, callback, state);
}
protected override bool OnEndWaitForChannel(IAsyncResult result)
{
Console.WriteLine("MyChannelListener<TChannel>.OnEndWaitForChannel()");
return this.InnerChannelListener.EndWaitForChannel(result);
}
protected override bool OnWaitForChannel(TimeSpan timeout)
{
Console.WriteLine("MyChannelListener<TChannel>.OnWaitForChannel()");
return this.InnerChannelListener.WaitForChannel(timeout);
}
public override Uri Uri
{
get
{
Console.WriteLine("MyChannelListener<TChannel>.Uri");
return this.InnerChannelListener.Uri;
}
}
protected override void OnAbort()
{
Console.WriteLine("MyChannelListener<TChannel>.OnAbort()");
this.InnerChannelListener.Abort();
}
protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)
{
Console.WriteLine("MyChannelListener<TChannel>.OnBeginClose()");
return this.InnerChannelListener.BeginClose(timeout, callback, state);
}
protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
{
Console.WriteLine("MyChannelListener<TChannel>.OnBeginOpen()");
return this.InnerChannelListener.BeginOpen(timeout, callback, state);
}
protected override void OnClose(TimeSpan timeout)
{
Console.WriteLine("MyChannelListener<TChannel>.OnClose()");
this.InnerChannelListener.Close(timeout);
}
protected override void OnEndClose(IAsyncResult result)
{
Console.WriteLine("MyChannelListener<TChannel>.OnEndClose()");
this.InnerChannelListener.EndClose(result);
}
protected override void OnEndOpen(IAsyncResult result)
{
Console.WriteLine("MyChannelListener<TChannel>.OnEndOpen()");
this.InnerChannelListener.EndOpen(result);
}
protected override void OnOpen(TimeSpan timeout)
{
Console.WriteLine("MyChannelListener<TChannel>.OnOpen()");
this.InnerChannelListener.Open(timeout);
}
}
}
5. 創建Custom Binding Element
我們知道Binding是Service mode layer進入Channel layer的中介,而Binding由一系列的Binding element組成。我們上面創建的Channel factory和Channel listener需要最終通過對應的BindingElement應用到Binding中才能最終發揮作用。我們就來創建這個BindingElement:MyBindingElement。
namespace Artech.ChannleStackExplore.Channels
{
public class MyBindingElement:BindingElement
{
public override BindingElement Clone()
{
return new MyBindingElement();
}
public override T GetProperty<T>(BindingContext context)
{
return context.GetInnerProperty<T>();
}
public override IChannelFactory<TChannel> BuildChannelFactory<TChannel>(BindingContext context)
{
Console.WriteLine("MyBindingElement.BuildChannelFactory()");
return new MyChannelFactory<TChannel>(context) as IChannelFactory<TChannel>;
}
public override IChannelListener<TChannel> BuildChannelListener<TChannel>(BindingContext context)
{
Console.WriteLine("MyBindingElement.BuildChannelListener()");
return new MyChannelListener<TChannel>(context) as IChannelListener<TChannel>;
}
}
}
夠簡單吧,直接調用MyChannelFactory和MyChannelListener的構造函數就可以了。
6. 創建Custom Binding
我們進入了最後的階段,創建一個Custom Binding。MyBinding繼承Binding class。 在CreateBindingElements方法中將我們的Binding element(MyBindingElement),連同其他必須的Binding element添加到BindingElementCollection 中。
namespace Artech.ChannleStackExplore.Channels
{
public class MyBinding:Binding
{
public override BindingElementCollection CreateBindingElements()
{
BindingElementCollection elemens = new BindingElementCollection();
elemens.Add(new TextMessageEncodingBindingElement());
elemens.Add(new MyBindingElement());
elemens.Add(new HttpTransportBindingElement());
return elemens.Clone();
}
public override string Scheme
{
get
{
return "http";
}
}
}
}
注:對BindElement的組裝可以通過configuration來實現。
7. 使用Custom Binding
我們上面所做的一切都匯集到我們的Custom binding:MyBinding。既然我們為之寫了那麼多代碼,我們一定要通過某種方式測試一下它時候具有我們需要的功能。我們通過MyBinding創建一個Messaging via Binding的應用。不熟悉的朋友可以轉到上一篇去熟悉一下。
Server端的代碼:
namespace Server
{
class Program
{
static void Main(string[] args)
{
MyBinding binding = new MyBinding();
IChannelListener<IReplyChannel> channelListener= binding.BuildChannelListener<IReplyChannel>(new Uri("http://127.0.0.1:8888/messagingviabinding"));
channelListener.Open();
while (true)
{
IReplyChannel channel= channelListener.AcceptChannel(TimeSpan.MaxValue);
channel.Open();
RequestContext context = channel.ReceiveRequest(TimeSpan.MaxValue);
Console.WriteLine("Receive a request message:\n{0}", context.RequestMessage);
Message replyMessage = Message.CreateMessage(MessageVersion.Soap12WSAddressing10, "http://artech.messagingviabinding", "This is a mannualy created reply message for the purpose of testing");
context.Reply(replyMessage);
channel.Close();
}
}
}
}
Client端的代碼:
namespace Client
{
class Program
{
static void Main(string[] args)
{
MyBinding binding = new MyBinding();
IChannelFactory<IRequestChannel> channelFactory = binding.BuildChannelFactory<IRequestChannel>();
channelFactory.Open();
IRequestChannel channel = channelFactory.CreateChannel(new EndpointAddress("http://127.0.0.1:8888/messagingviabinding"));
channel.Open();
Message requestMessage = Message.CreateMessage(MessageVersion.Soap12WSAddressing10, "http://artech.messagingviabinding", "This is a mannualy created reply message for the purpose of testing");
Message replyMessage = channel.Request(requestMessage);
Console.WriteLine("Receive a reply message:\n{0}", replyMessage);
channel.Close();
channelFactory.Close();
Console.Read();
}
}
}
運行的結果將會是這樣:
Server:
Client:
通過上面的輸出結果,你很直觀的了解到了整個程序執行過程中,我們的定義在Channel,Channel factory,Channel listener和Binding element的方法是如何被依次執行的。
本文配套源碼