3.3.4 案例演示:如何自定义信道
WCF是一个极具扩展性的通信框架,反映在信道层上就是可以自由地创建信道,利用这些自定义信道完成某项消息处理的功能。比如,可以通过自定义信道对消息进行压缩。
上述的3种消息交换模式中,请求-回复模式最为典型。在该模式下,消息发送端和接收端的信道分别实现了IRequstChannel和IReplyChannel接口。
本案例将演示如何自定义RequestChannel和ReplyChannel。在自定义信道的方法和属性中,仅仅通过System.Console在控制台上打印出方法或属性名称。这样做的目的有两个,其一是为了简单起见,因为本案例的目的在于演示信道是如何自定义的;其二就是当我们把信道应用于真正的WCF应用时,可以根据打印出来的类型指导具体执行的方法和执行的先后顺序。为此先定义如下一个静态的PrintHelper类,静态Print方法打印出传入实例的类型和方法(或者属性名称):
using System; namespace Artech.CustomChannels { public static class PrintHelper { public static void Print(object instance, string method) { Console.WriteLine("{0}.{1}", instance.GetType().Name, method); } } }
自定义RequestChannel
自定义请求信道SimpleRequestChannel实现IRequestChannel,并直接继承自ChannelBase。
using System; using System.ServiceModel.Channels; using System.ServiceModel; namespace Artech.CustomChannels { public class SimpleRequestChannel.:ChannelBase,IRequestChannel { //方法成员 } }
信道并不能孤立地存在,它存在于一个由多个信道对象连接而成的信道栈中。所以,对于一个不处于栈尾的信道来说,在处理完消息后,一般会把处理后的消息传递给后一个信道。反映在方法实现上,需要在执行本信道的某个方法之后,获得下一个信道对象,调用同名的方法。所以在SimpleRequestChannel,我们需要定义一个特殊的字段成员:_innerChannel。该字段表示信道栈中的下一个信道,该字段在构造函数中指定。在构造函数中还须要指定一
个ChannelManagerBase对象,表示基于信道的信道工厂。
using System; using System.ServiceModel.Channels; using System.ServiceModel; namespace Artech.CustomChannels { public class SimpleRequestChannel.:ChannelBase,IRequestChannel { private IRequestChannel _innerChannel; public SimpleRequestChannel.(ChannelManagerBase channelManager, IRequestChannel innerChannel) : base(channelManager) { PrintHelper.Print(this, "SimpleRequestChannel."); this._innerChannel = innerChannel; } //其他成员 }
ChannelBase是一个抽象类,它定义一系列必须实现的protected抽象方法或属性(实际上这些受保护的抽象方法或属性定义在CommunicationObject中,ChannelBase继承自CommunicationObject)。我们须要实现从基类继承下来的一系列OnXxx、OnBeginXxx、OnEndXxx方法。
using System; using System.ServiceModel.Channels; using System.ServiceModel; namespace Artech.CustomChannels { public class SimpleRequestChannel.:ChannelBase,IRequestChannel { //其他成员 protected override void OnAbort() { PrintHelper.Print(this, "OnAbort"); this._innerChannel.Abort(); } protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state) { PrintHelper.Print(this, "OnBeginClose"); return this._innerChannel.BeginClose(timeout,callback,state); } protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state) { PrintHelper.Print(this, "OnBeginOpen"); return this._innerChannel.BeginOpen(timeout, callback, state); } protected override void OnClose(TimeSpan timeout) { PrintHelper.Print(this, "OnClose"); this._innerChannel.Close(timeout); } protected override void OnEndClose(IAsyncResult result) { PrintHelper.Print(this, "OnEndClose"); this._innerChannel.EndClose(result); } protected override void OnEndOpen(IAsyncResult result) { PrintHelper.Print(this, "OnEndOpen"); this._innerChannel.EndOpen(result); } protected override void OnOpen(TimeSpan timeout) { PrintHelper.Print(this, "OnOpen"); this._innerChannel.Open(timeout); } } }
SimpleRequestChannel实现了IRequestChannel接口,所以还须实现IRequestChannel所有的方法和属性。
using System; using System.ServiceModel.Channels; using System.ServiceModel; namespace Artech.CustomChannels { public class SimpleRequestChannel.:ChannelBase,IRequestChannel { //其他成员 #region IRequestChannel Members public IAsyncResult BeginRequest(Message message, TimeSpan timeout, AsyncCallback callback, object state) { PrintHelper.Print(this, "BeginRequest"); return this._innerChannel.BeginRequest(message,timeout, callback,state); } public IAsyncResult BeginRequest(Message message, AsyncCallback callback, object state) { PrintHelper.Print(this, "BeginRequest"); return this._innerChannel.BeginRequest(message, callback, state); } public Message EndRequest(IAsyncResult result) { PrintHelper.Print(this, "EndRequest"); return this._innerChannel.EndRequest(result); } public EndpointAddress RemoteAddress { get { PrintHelper.Print(this, "RemoteAddress"); return this._innerChannel.RemoteAddress; } } public Message Request(Message message, TimeSpan timeout) { PrintHelper.Print(this, "Request"); return this._innerChannel.Request(message,timeout); } public Message Request(Message message) { PrintHelper.Print(this, "Request"); return this._innerChannel.Request(message); } public Uri Via { get { PrintHelper.Print(this, "Via"); return this._innerChannel.Via; } } #endregion } }
自定义ReplyChannel
与SimpleRequestChannel类似,自定义的ReplyChannel(SimpleRequestChannel)继承自ChannelBase,实现IReplyChannel接口,并以同样的方式实现了从基类和接口中继承的方法和属性。
using System; using System.ServiceModel.Channels; namespace Artech.CustomChannels { public class SimpleReplyChannel:ChannelBase,IReplyChannel { private IReplyChannel _innerChannel; public SimpleReplyChannel(ChannelManagerBase channelManager, IReplyChannel innerChannel) : base(channelManager) { PrintHelper.Print(this, "SimpleReplyChannel"); this._innerChannel = innerChannel; } protected override void OnAbort() { PrintHelper.Print(this, "OnAbort"); this._innerChannel.Abort(); } public IAsyncResult BeginReceiveRequest(TimeSpan timeout, AsyncCallback callback, object state) { PrintHelper.Print(this, "BeginReceiveRequest"); return this._innerChannel.BeginReceiveRequest(timeout, callback,state); } //其他成员 } }
自定义DuplexSessionChannel
为了演示自定义会话信道,我们创建一个继承自ChannelBase并实现了IDuplexSessionChannel接口的自定义DuplexSessionChannel:SimpleDuplexSessionChannel。它的实现方式与自定义RequestChannel和ReplyChannel类似,不同的是innerChannel类型为IDuplexSessionChannel。
using System; using System.ServiceModel.Channels; using System.ServiceModel; namespace Artech.CustomChannels { public class SimpleDuplexSessionChannel:ChannelBase,IDuplexSessionChannel { private IDuplexSessionChannel _innerChannel; public SimpleDuplexSessionChannel(ChannelManagerBase channelManager, IDuplexSessionChannel innerChannel) : base(channelManager) { PrintHelper.Print(this, "SimpleDuplexSessionChannel"); this._innerChannel = innerChannel; } protected override void OnAbort() { PrintHelper.Print(this, "OnAbort"); this._innerChannel.Abort(); } //其他成员 public override T GetProperty<T>() { return this._innerChannel.GetProperty<T>(); } #endregion } }
SimpleDuplexSessionChannel除了实现从基类中继承的抽象方法和属性,以及定义在接口中的方法和属性成员之外,还须要重写GetProperty<T>()方法。该方法用于获取信道某种类型的属性,是探测信道是否具有某种功能和性能的手段。我们须要通过调用后续信道的同名方法来重新实现该方法。