WCF技术剖析(卷1)
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

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>()方法。该方法用于获取信道某种类型的属性,是探测信道是否具有某种功能和性能的手段。我们须要通过调用后续信道的同名方法来重新实现该方法。