創建自定義通道
上一節已經看過了通道層裡的基本類型,現在我們就來創建一個自定義通道 。這個通道的目的就是要在控制台窗口裡打印一個文本。因此,我們構建的通道 對於演示通道的生命周期和程序調用不同的通道成員的時候都會非常有用。因為 我們的自定義通道會在控制台窗口打印文本,所以有必要傳遞通道方法的委托調 用給堆棧裡的下一個通道。我們會把這個通道成為委托通道(DelegatorChannel )。在開始之前,有必要指出的是,這裡還看不到一個通道運行必須的全部代碼 ,直到第8章“綁定”會全部給出。沒辦法,創建通道還需要額外的工作。
創建自定義通道首要考慮的問題就是形狀或者是通道要支持的形狀。 DelegatorChannel必須支持所有的通道形狀(IInputChannel、 IOutputChannel 、IDuplexChannel、IReplyChannel、IRequestChannel以及所有的會話變量)。 因此,我們必須構建多個通道,而且這些通道有固定的層次關系。
創建基類型
因為所有的通道都要使用通道狀態機,並且每個通道必須保留堆棧裡下一個 通道實例的引用,因此把這些屬性的聲明放在一個基類裡比較合理。所有的通道 類型都會繼承自這個基類型,所以把基類型定義為泛型更加合適。考慮到這些需 求,我把基類型命名為DelegatorChannelBase<TShape>,TShape 必須是 一個引用類型,而且繼承自IChannel。(記住所有的通道類型都繼承自 IChannel。)DelegatorChannelBase<TShape>的是ChannelBase的子類型 ,因為這樣它就會使用公共的狀態機,並且可以實現Binding 的超時屬性。 DelegatorChannelBase<TShape>的初始定義如下:
internal class DelegatorChannelBase<TShape> : ChannelBase
where TShape : class, IChannel {
// implementation not shown yet
}
添加構造函數
DelegatorChannelBase<TShape>對象不能放在堆棧的底部。換句話說 ,DelegatorChannelBase<TShape>對象必須擁有一個通道對象的引用,而 且泛型類型表示的是通道形狀,我們會把泛型類型作為構造函數的參數。當然構 造函數還需要一個通道工廠的引用。當然,一個原因就是為了便於實現綁定的( time-out)超時屬性。另外一個原因就是為了在創建通道完畢的時候可以通知一 下通道工廠。你會在第7章裡看到更多內容。基類的構造函數定義如下:
internal class DelegatorChannelBase<TShape> : ChannelBase
where TShape : class, IChannel {
private TShape _innerChannel; // reference the next channel in the stack
private String _source; // part of the String to print to the console
protected DelegatorChannelBase(ChannelManagerBase channelManagerBase,
TShape innerChannel,
String source) : base(channelManagerBase){
if(innerChannel == null) {
throw new ArgumentNullException("DelegatorChannelBase requires a non-null channel.",
"innerChannel");
}
// set part of the String to print to console
_source = String.Format("{0} CHANNEL STATE CHANGE: DelegatorChannelBase", source);
// set the reference to the next channel
_innerChannel = innerChannel;
}
// other implementation not shown yet
}
注意_innerChannel和_source成員變量。像注釋說的,這些成員變量是為了 存儲下一個通道的引用和要打印的字符。構造函數的第一個參數是 ChannelManagerBase類型。ChannelManagerBase的引用通過ChannelBase構造函 數存儲起來。
添加狀態機
因為DelegatorChannelBase<TShape>繼承自抽象類型ChannelBase,並 且ChannelBase繼承自抽象類型CommunicationObject但沒有實現 CommunicationObject裡定義的成員,因此 DelegatorChannelBase<TShape>類型必須實現這些抽象成員。因此 DelegatorChannelBase<TShape>裡的所有狀態轉換必須傳播到堆棧裡的其 它通道,我們的狀態轉換方法委托會調用 innerChannel通道變量,如下所示:
internal class DelegatorChannelBase<TShape> : ChannelBase
where TShape : class, IChannel {
private TShape _innerChannel; // reference to the next channel
private String _source; // part of the String to output
// provide the _innerChannel to derived types
protected TShape InnerChannel {
get { return _innerChannel; }
}
protected DelegatorChannelBase(ChannelManagerBase channelManagerBase,
TShape innerChannel,
String source) : base(channelManagerBase){
if(innerChannel == null) {
throw new ArgumentNullException("DelegatorChannelBase requires a non-null channel.",
"innerChannel");
}
// set part of the String to print to console
_source = String.Format("{0} CHANNEL STATE CHANGE: DelegatorChannelBase", source);
// set the reference to the next channel
_innerChannel = innerChannel;
}
// IChannel implementation
public override T GetProperty<T>() {
return this._innerChannel.GetProperty<T>();
}
#region CommunicationObject members
protected override void OnAbort() {
PrintHelper.Print(_source, "OnAbort");
this._innerChannel.Abort();
}
protected override IAsyncResult OnBeginClose(TimeSpan timeout,
AsyncCallback callback,
Object state) {
// output that the method was called
PrintHelper.Print( _source, "OnBeginClose");
// delegate the call to the next channel
return this._innerChannel.BeginClose(timeout, callback, state);
}
protected override IAsyncResult OnBeginOpen(TimeSpan timeout,
AsyncCallback callback,
Object state) {
// output that the method was called
PrintHelper.Print(_source, "OnBeginOpen");
// delegate the call to the next channel
return this._innerChannel.BeginOpen(timeout, callback, state);
}
protected override void OnClose(TimeSpan timeout) {
// output that the method was called
PrintHelper.Print(_source, "OnClose");
// delegate the call to the next channel
this._innerChannel.Close(timeout);
}
protected override void OnEndClose(IAsyncResult result) {
// output that the method was called
PrintHelper.Print(_source, "OnEndClose");
// delegate the call to the next channel
this._innerChannel.EndClose(result);
}
protected override void OnEndOpen(IAsyncResult result) {
// output that the method was called
PrintHelper.Print(_source, "OnEndOpen");
// delegate the call to the next channel
this._innerChannel.EndOpen(result);
}
protected override void OnOpen(TimeSpan timeout) {
// output that the method was called
PrintHelper.Print(_source, "OnOpen");
// delegate the call to the next channel
this._innerChannel.Open(timeout);
}
#endregion
}
每個狀態轉換方法(OnAbort、OnBeginClose、OnBeginOpen、OnClose、 OnEndClose、OnEndOpen和OnOpen)都會調用通道上對應的狀態轉換方法。每個 狀態轉換方法也可以調用PrintHelper類型上的靜態方法Print。PrintHelper類 型只做在控制台窗口上打印字符的工作。
創建數據報通道
既然我們已經定義了通道的基類型,現在就定義數據報消息交換模式的通道 吧。因為數據報發送通道必須繼承IInputChannel接口,接收通道必須繼承 IOutputChannel接口,所以我們需要DelegatorChannelBase<TShape>來繼 承這2個接口並實現他們。因為數據報接口被用作雙工通信的接口,同時也會做 會雙工會話通道接口,所以我們把數據報通道定義為泛型。
注釋:我們會從接收者開始,然後定義發送者。為了簡明扼要,我就不把所 有的代碼都貼出來,而是著重展示代碼裡的實現模式。
數據報接收通道
數據報接收通道繼承自DelegatorChannelBase<TShape>類型並且實現 了IInputChannel 接口。和DelegatorChannelBase<TShape>一樣,我們的 數據報接收通道也會被定義為泛型類型,因此允許被雙工通道復用,同樣也可以 被數據報和雙工變量成員使用。因為這些需求,數據報接收通道命名為 DelegatorInputChannel<TShape>,如下所示:
internal class DelegatorInputChannel<TShape> :
DelegatorChannelBase<TShape>, IInputChannel
where TShape:class, IInputChannel {
// implementation not shown
}
DelegatorInputChannel<TShape>構造函數必須調用基類的構造函數, 設置輸出String的值,並且調用PrintHelper.Print方法,如下所示:
internal class DelegatorInputChannel<TShape> :
DelegatorChannelBase<TShape>, IInputChannel
where TShape:class, IInputChannel {
private String _source; // store the String to output
internal DelegatorInputChannel(ChannelManagerBase channelManagerBase,
TShape innerChannel,
String source) : base(channelManagerBase,
innerChannel,
source) {
// assign the name and generic parameter to the String
_source = String.Format("{0} CHANNEL: DelegatorInputChannel<{1}>",
source,
typeof (TShape).Name);
// output that the method was called
PrintHelper.Print(_source, "ctor");
}
// other implementation not shown
}
下面我們需要實現IInputChannel接口,為了簡略起見,我只列舉三個成員:
public IAsyncResult BeginReceive(TimeSpan timeout,
AsyncCallback callback,
Object state) {
// output that the method was called
PrintHelper.Print(_source, "BeginReceive");
// delegate the call to the next channel
return this.InnerChannel.BeginReceive(timeout, callback, state);
}
public IAsyncResult BeginReceive(AsyncCallback callback, Object state) {
// output that the method was called
PrintHelper.Print(_source, "BeginReceive");
// delegate the call to the next channel
return this.InnerChannel.BeginReceive(callback, state);
}
public IAsyncResult BeginTryReceive(TimeSpan timeout,
AsyncCallback callback,
Object state) {
// output that the method was called
PrintHelper.Print(_source, "BeginTryReceive");
// delegate the call to the next channel
return this.InnerChannel.BeginTryReceive(timeout, callback, state);
}
DelegatorInputChannel<TShape>類型只有在其它成員定義結束以後才 會完整(EndReceive、EndTryReceive、EndWaitForMessage、 LocalAddress、 Receive、TryReceive和WaitForMessage)。
數據報發送通道
數據報發送通道與接收通道很相似,區別僅僅在於它實現了IOutputChannel 接口。為了避免重復,我們會給出這個類型定義,而 DelegatorInputChannel<TShape>實現則交給讀者來做:
internal class DelegatorOutputChannel<TShape> :
DelegatorChannelBase<TShape>, IOutputChannel where
TShape: class, IOutputChannel {
private String _source; // store the String to output
internal DelegatorOutputChannel(ChannelManagerBase channelManagerBase,
TShape innerChannel,
String source) : base(channelManagerBase,
innerChannel,
source) {
_source = String.Format("{0} CHANNEL: DelegatorOutputChannel<{1}>", source,
typeof(TShape).Name);
// output that the method was called
PrintHelper.Print(_source, "ctor");
}
#region IOutputChannel Members
public IAsyncResult BeginSend(Message message,
TimeSpan timeout,
AsyncCallback callback,
Object state) {
// output that the method was called
PrintHelper.Print(_source, "BeginSend");
// delegate the call to the next channel
return this.InnerChannel.BeginSend(message, timeout, callback, state);
}
public IAsyncResult BeginSend(Message message, AsyncCallback callback, object state) {
// output that the method was called
PrintHelper.Print(_source, "BeginSend");
// delegate the call to the next channel
return this.InnerChannel.BeginSend(message, callback, state);
}
public void EndSend(IAsyncResult result) {
// output that the method was called
PrintHelper.Print(_source, "EndSend");
// delegate the call to the next channel
this.InnerChannel.EndSend(result);
}
public EndpointAddress RemoteAddress {
get {
// output that the method was called
PrintHelper.Print(_source, "RemoteAddress");
// delegate the call to the next channel
return this.InnerChannel.RemoteAddress; }
}
public void Send(Message message, TimeSpan timeout) {
// output that the method was called
PrintHelper.Print(_source, "Send");
// delegate the call to the next channel
this.InnerChannel.Send(message, timeout);
}
public void Send(Message message) {
// output that the method was called
PrintHelper.Print(_source, "Send");
// delegate the call to the next channel
this.InnerChannel.Send(message);
}
public Uri Via {
get {
// output that the method was called
PrintHelper.Print(_source, "Via");
// delegate the call to the next channel
return this.InnerChannel.Via;
}
}
#endregion
}
雙工通道
想一下通道的形狀,記得IDuplexChannel接口是IInputChannel 和 IOutputChannel的結合體。因為我們已經有了一個類型實現了IInputChannel 和 IOutputChannel,所以我們可以服用這個類型作為我們的基類。與 IOutputChannel 相比,IInputChannel接口包含更多的成員,所以(沒有意外的 話)DelegatorInputChannel<TShape>類型會作為我們的雙工通信的基類 型。
因為雙工通道實現了IDuplexChannel接口,我們來調用 DelegatorDuplexChannel雙工通道,並且在基類裡使用IDuplexChannel作為泛型 參數,如下所示:
internal class DelegatorDuplexChannel :
DelegatorInputChannel<IDuplexChannel>, IDuplexChannel {
// implementation not shown yet
}
Because the DelegatorDuplexChannel is very similar to the DelegatorInputChannel<TShape> type definition, I will show only part of the type definition here:
DelegatorDuplexChannel與DelegatorInputChannel<TShape>的定義很 類似,我這裡只展示類型定義部分:
internal class DelegatorDuplexChannel :
DelegatorInputChannel<IDuplexChannel>, IDuplexChannel {
private String _source; // store the String to output
internal DelegatorDuplexChannel(ChannelManagerBase channelManagerBase,
// use IDuplexSession as the 2nd parameter
IDuplexChannel innerChannel,
String source) : base(channelManagerBase,
innerChannel,
source) {
_source = String.Format("{0} CHANNEL: DelegatorDuplexChannel", source);
PrintHelper.Print(_source, "ctor");
}
#region IOutputChannel Members
public IAsyncResult BeginSend(Message message,
TimeSpan timeout,
AsyncCallback callback,
Object state) {
PrintHelper.Print(_source, "BeginSend");
return this.InnerChannel.BeginSend(message, timeout, callback, state);
}
// other IOutputChannel Members omitted for brevity
#endregion
}
雙工會話通道
從對象模型的角度來看,會話通道形狀與非會話通道只有一些細微的差別。 例如,IDuplexSessionChannel是IDuplexChannel和 ISessionChannel<IDuplexSession>的結合體。因為我們已有有了一個 DelegatorDuplexChannel類型定義(它實現了IDuplexChannel接口),創建一個 會話通道僅僅是一個繼承DelegatorDuplexChannel並實現 IDuplexSessionChannel接口的問題,如下所示:
internal sealed class DelegatorDuplexSessionChannel :
DelegatorDuplexChannel, IDuplexSessionChannel {
private IDuplexSessionChannel _innerSessionChannel; // reference the next
// sessionful channel
private String _source; // store the String to output
internal DelegatorDuplexSessionChannel(ChannelManagerBase
channelManagerBase, IDuplexSessionChannel innerChannel, String source)
: base(channelManagerBase, innerChannel, source) {
_source = String.Format("{0} CHANNEL: DelegatorDuplexSessionChannel",
source);
PrintHelper.Print(_source, "ctor");
// assign the reference to the next sessionful channel
this._innerSessionChannel = innerChannel;
}
// IDuplexSessionChannel member that is not defined in IDuplexChannel
public IDuplexSession Session {
get {
PrintHelper.Print(_source, "Session");
return this._innerSessionChannel.Session; }
}
}
DelegatorDuplexChannel包含一個IDuplexChannel類型的成員變量,我們需 要通過一個IDuplexSessionChannel類型的局部變量來存儲同一個對象的引用。 這樣做可以使得我們容易地添加Session屬性到我們的類型定義上。
注釋:有了DelegatorChannelBase<TShape>, DelegatorInputChannel<TShape>, DelegatorOutputChannel<TShape>,DelegatorDuplexChannel 和 DelegatorDuplexSessionChannel裡使用的模式,我們就可以很容易地添加 IInputSessionChannel、 IOutputSessionChannel、 IRequestChannel、 IReplyChannel、 IRequestSessionChannel和IReplySessionChannel的實現代碼 。因為有些類型是創建通道時必備的,所以下面兩章,我們會構建一些WCF程序 來使用這些類型。
本章小結
因為實際上,通道來執行消息相關的工作,所以盡管WCF開發人員不會看到這 些,但是它們仍然是所有WCF程序必不可少的部分,。這一章裡,我們學習了通 道狀態機,通道API裡的關鍵類型,以及自定義通道。在第7章和第8章裡,我們 會繼續學習如何在WCF程序裡使用我們的自定義通道。