《WCF技术内幕》翻译34:第2部分_第6章_通道:通道接口和基本类型

简介:
本节目录:
 

通道接口和基本类型

本章开始部分曾经提到过,学习WCF通道基础结构的一个关键部分就是了解WCF系统在通道层使用的接口和类型。本节系统整理了这些复杂的类型系统,深入浅出地讲述各个接口和类型的原理,使得读者可以更容易掌握这些知识点。

IChannel接口

System.ServiceModel.Channels.IChannel接口看似简单,但是它对于通道层的实现至关重要。所有的通道和通道工厂必须实现它。换句话说,一个集成了CommunicationObject的类型通常也会实现IChannel接口。在详细学习IChannel接口的作用以前,我们先来看看它的基本结构:
 
InBlock.gif public interface IChannel : ICommunicationObject {
InBlock.gif            T GetProperty<T>() where T: class;
InBlock.gif}
你或许会问自己:“为什么会这么重要呢?”记得 CommunicationObject堆栈里的每个CommunicationObject对象都有一些特定的功能,并且只有栈顶的通道才可以被调用者调用。当堆栈组合正常的情况啊,GetProperty<T>方法提供了在CommunicationObject堆栈里查询特定功能的途径。例如,你也许想知道CommunicationObject堆栈是否支持特定的通道外形,MessageVersion或安全功能。下面代码演示了调用如何使用IChannel.GetProperty<T>方法:
// assume channel stack (myChannelStack) created假定通道堆栈已经创建完毕
InBlock.gifMessageVersion messageVersion = 
InBlock.gif myChannelStack.GetProperty<MessageVersion>(); 
InBlock.gif if(MessageVersion !=  null){ 
InBlock.gif  // do something 
InBlock.gif
InBlock.gif // app continues
CommunicationObject堆栈里的其它成员一样,当一个通道不知道如何响应查询的时候,它会使用委托去调用堆栈里的下一个通道。GetProperty<T>的简单实现如下:
InBlock.gif public  override T GetProperty<T>() where T:  class { 
InBlock.gif             if ( typeof(T) ==  typeof(MessageVersion)) { 
InBlock.gif                         // this type knows only how to return MessageVersion 
InBlock.gif                         return (T)  this.MessageVersion; 
InBlock.gif         } 
InBlock.gif             // no other capabalities are known here, so 
InBlock.gif             // delegate the query to the next node 
InBlock.gif             return  this.inner.GetProperty<T>(); 
InBlock.gif}
如上所示,这个 GetProperty<T>方法的实现可以只返回MessageVersion,并且它的可以调用查询堆栈里的下一个通道的功能。如果查询的功能不存在,就会返回null,而不是抛出异常。因为使用了委托来嵌套查询,所以只有最底层的通道查询方法才会抛出null。

数据报通道:IInputChannel与IOutputChannel

第三张里曾经提到,数据报消息交换模式非常强大而且极具可伸缩性。在数据报消息交换模式里,发送者发送一个消息到接收者,而不期望得到回复。更简单地说,发送者输出(发送)一个消息,接收者接受一个消息作为输入。因此, WCF基础结构定义了数据报交换模式里的发送者接口名为System.ServiceModel.Channels.IOutputChannel,而接受者的接口名为System.ServiceModel.IInputChannel。

发送接口:IOutputChannel

Like its role in the Datagram MEP, the IOutputChannel interface is simple, as shown here:
和其在数据报交换模式里角色一样,IOutputChannel接口比较简单,如下所示:
InBlock.gif public  interface IOutputChannel : IChannel, ICommunicationObject { 
InBlock.gif            IAsyncResult BeginSend(Message message, AsyncCallback callback, 
InBlock.gif                                                         Object state); 
InBlock.gif            IAsyncResult BeginSend(Message message, TimeSpan timeout, 
InBlock.gif                                                         AsyncCallback callback, Object state); 
InBlock.gif             void EndSend(IAsyncResult result); 
InBlock.gif             void Send(Message message); 
InBlock.gif             void Send(Message message, TimeSpan timeout); 
InBlock.gif 
InBlock.gif            EndpointAddress RemoteAddress { get; } 
InBlock.gif            Uri Via { get; } 
InBlock.gif}
首先, IOutputChannel实现了IChannel和ICommunicationObject接口。任何实现了IOutputChannel接口的类型,都必须定义公有的通道状态机成员和GetProperty<T>方法。为了支持异步编程模型(APM),接口定义了同步和异步的Send方法。
RemoteAddress属性指的是消息发送的地址。值得注意的是,这不一定是消息发送的真实地址。回忆一下第2章“面向服务”里的邮政服务的例子,这在一个消息接收者的情况下,对于标记地址十分有用。IOutputChannel上Via属性表示的另外一个地址是消息发送的目标地址。

接收接口:IInputChannel

接收数据报消息的通道实现了IInputChannel接口。对于接收者在数据报交换模式里的角色,IInputChannel只定义了接收成员而没有发送成员。IInputChannel接口的定义如下:
InBlock.gif public  interface IInputChannel : IChannel, ICommunicationObject { 
InBlock.gif EndpointAddress LocalAddress { get; } 
InBlock.gif 
InBlock.gif  // Receive Methods 
InBlock.gif IAsyncResult BeginReceive(AsyncCallback callback, Object state); 
InBlock.gif IAsyncResult BeginReceive(TimeSpan timeout, AsyncCallback callback, 
InBlock.gif                                                        Object state); 
InBlock.gif Message EndReceive(IAsyncResult result); 
InBlock.gif Message Receive(); 
InBlock.gif Message Receive(TimeSpan timeout); 
InBlock.gif 
InBlock.gif  // TryReceive Methods 
InBlock.gif IAsyncResult BeginTryReceive(TimeSpan timeout, AsyncCallback callback, 
InBlock.gif                                                             Object state); 
InBlock.gif  bool EndTryReceive(IAsyncResult result,  out Message message); 
InBlock.gif  bool TryReceive(TimeSpan timeout,  out Message message); 
InBlock.gif 
InBlock.gif  // Waiting Methods 
InBlock.gif IAsyncResult BeginWaitForMessage(TimeSpan timeout, 
InBlock.gif                                                                     AsyncCallback callback, 
InBlock.gif                                                                     Object state); 
InBlock.gif  bool EndWaitForMessage(IAsyncResult result); 
InBlock.gif  bool WaitForMessage(TimeSpan timeout); 
InBlock.gif}
通常,接收程序会消极地等待消息到来。为此, IInputChannel定义了三个等待消息的方法。这些方法的命名没有什么规律,但是为了简便,这里就分为Receive、TryReceive和WaitForMessage几组方法。所有的方法都包含同步和异步定义。
Receive方法等待一段时间,如果消息在这段时间内到达,该方法会返回一个Message引用。如果在规定的时间内,消息还没到达,这些方法就会抛出一个TimeoutException。TryReceive方法会等待一段时间,然后通过out参数返回一个Message引用。这些方法返回一个Boolean值表示能否在期望的时间内返回Message。Receive和TryReceive方法最大的不同就是如何显示超时结果。
Receive和TryReceive 不同,WaitForMessage方法不会返回一个Message引用,或者一个out参数。它会返回一个表示一个消息是否到达的Boolean值。这有点像I/O基础结构里的Peek功能。把WaitForMessage与Receive或TryReceive一起使用,可以实现等待一个消息并接受一个消息。
当消息要参与到一些其他的活动中的时候,WaitForMessage方法就非常有用。比如,思考以下情况,当一个Message必须参与到一个事务里。在这个例子里,对于Receive和TryReceive方法的调用必须包装到事物里。如果Message没有到达,调用者必须终止事务。如果,调用者使用了WaitForMessage方法,这次调用就没必要发生在事务的范围内。如果WaitForMessage返回false,调用者仅仅需要再调用WaitForMessage方法。一旦Message到达,调用者能够启动一个事务,然后调用Receive或TryReceive方法执行相应的任务。

请求/应答通道:IRequestChannel和IReplyChannel

在请求/应答消息交换模式里,消息的参与者都要发送和接收消息。发送者发送消息给接收者,然后等待回复。而接收者会接收请求消息,然后发送一个回复消息。为了实现通道形状,IRequestChannel和IReplyChannel接口分别定义了符合请求/应答消息交换模式的成员.

发送接口:IRequestChannel


IRequestChannel接口定义了发送请求消息和接收应答消息的相关成员。通道层里发送和接收消息的成员都包含同步和异步的定义。如下所示:
InBlock.gif public  interface IRequestChannel : IChannel, ICommunicationObject { 
InBlock.gif  // Request Methods 
InBlock.gif IAsyncResult BeginRequest(Message message, AsyncCallback callback, 
InBlock.gif                                                        Object state); 
InBlock.gif IAsyncResult BeginRequest(Message message, TimeSpan timeout, 
InBlock.gif                                                        AsyncCallback callback, Object state); 
InBlock.gif Message EndRequest(IAsyncResult result); 
InBlock.gif Message Request(Message message); 
InBlock.gif Message Request(Message message, TimeSpan timeout); 
InBlock.gif 
InBlock.gif EndpointAddress RemoteAddress { get; } 
InBlock.gif Uri Via { get; } 
InBlock.gif}
上面的代码里, Request方法接受一个Message类型的参数,然后返回一个Message类型的实例。 这些成员方法的签名保证了它们符合请求/应答消息交换模式。

接收接口:IReplyChannel

支持请求/应答消息交换模式的消息接收程序必须实现IReplyChannel接口,IReplyChannel的定义如下:
InBlock.gif public  interface IReplyChannel : IChannel, ICommunicationObject { 
InBlock.gif RequestContext ReceiveRequest(); 
InBlock.gif RequestContext ReceiveRequest(TimeSpan timeout); 
InBlock.gif IAsyncResult BeginReceiveRequest(AsyncCallback callback, Object state); 
InBlock.gif IAsyncResult BeginReceiveRequest(TimeSpan timeout, 
InBlock.gif                                                                     AsyncCallback callback, Object state); 
InBlock.gif RequestContext EndReceiveRequest(IAsyncResult result); 
InBlock.gif 
InBlock.gif Boolean TryReceiveRequest(TimeSpan timeout,  out RequestContext context); 
InBlock.gif IAsyncResult BeginTryReceiveRequest(TimeSpan timeout, 
InBlock.gif                                                                            AsyncCallback callback, 
InBlock.gif                                                                            Object state); 
InBlock.gif Boolean EndTryReceiveRequest(IAsyncResult result, 
InBlock.gif                                                              out RequestContext context); 
InBlock.gif 
InBlock.gif Boolean WaitForRequest(TimeSpan timeout); 
InBlock.gif IAsyncResult BeginWaitForRequest(TimeSpan timeout, 
InBlock.gif                                                                     AsyncCallback callback, 
InBlock.gif                                                                     Object state); 
InBlock.gif  bool EndWaitForRequest(IAsyncResult result); 
InBlock.gif 
InBlock.gif EndpointAddress LocalAddress { get; } 
InBlock.gif}
IReplyChannel里没有直接返回一个 Message实例的成员。相反,IReplyChannel接口支持通过RequestContext类型访问接收到的Message实例。下一节会详细讨论RequestContext类型。现在,我们该知道接收到的消息对于RequestContext类型是可见的,并且可以通过RequestContext访问消息实例。
IInputChannel一样,IReplyChannel也定义了几类接收消息的方法。ReceiveRequest方法返回一个RequestContext实例,并且超时的时候,会抛出异常。TryReceiveRequest会返回一个Boolean类型的值来表示是否在规定的时间内接收到消息。WaitForRequest方法,和IInputChannel接口上的WaitForMessage方法类似,返回的结果取决于请求消息或是否超时。

请求/应答关联:RequestContext类型

在请求/应答消息交换模式里,请求和应答是紧密关联的。从发送者的角度来看,请求通常会返回一个应答消息。从接受者的角度来看,一个接收到的消息必须产生一个应答消息。如前所述,IReplyChannel使用RequestContext作为ReceiveRequest方法的返回类型。这是请求/应答消息交换模式下,接收通道关联消息的首要方式。
更高层次上,RequestContext类型包装了请求消息,而且提供了发送应答消息给发送者的方法。在RequestContext里,可以通过RequestMessage属性查看请求消息。RequestContext的Reply方法提供了发送应答消息的途径。和其它的通道成员一样,reply方法对于同步和异步方法都是可见的。下面代码展示了RequestContext类型的成员:
InBlock.gif public  abstract  class RequestContext : IDisposable { 
InBlock.gif  protected RequestContext(); 
InBlock.gif 
InBlock.gif  public  abstract  void Abort(); 
InBlock.gif 
InBlock.gif  public  abstract  void Reply(Message message); 
InBlock.gif  public  abstract  void Reply(Message message, TimeSpan timeout); 
InBlock.gif  public  abstract IAsyncResult BeginReply(Message message, 
InBlock.gif                                                                                    AsyncCallback callback, 
InBlock.gif                                                                                    Object state); 
InBlock.gif  public  abstract IAsyncResult BeginReply(Message message, 
InBlock.gif                                                                                    TimeSpan timeout, 
InBlock.gif                                                                                    AsyncCallback callback, 
InBlock.gif                                                                                    Object state); 
InBlock.gif  public  abstract  void EndReply(IAsyncResult result); 
InBlock.gif 
InBlock.gif  public  abstract  void Close(); 
InBlock.gif  public  abstract  void Close(TimeSpan timeout); 
InBlock.gif 
InBlock.gif  protected  virtual  void Dispose(Boolean disposing); 
InBlock.gif  void IDisposable.Dispose(); 
InBlock.gif 
InBlock.gif  public  abstract Message RequestMessage { get; } 
InBlock.gif}
如代码所示,  RequestContext实现了IDisposable接口。因为通道层里其它成员没有实现IDisposable接口,所以这里就很难看出为什么RequestContext类型会这么干。RequestContext类型实现IDisposable接口是因为RequestContext包含了一个Message实例。第4章里“WCF101”里曾经讨论过,Message实例包含一个Stream,因此必须实现IDisposable接口。因为这种关系,RequestContext类型上的Dispose方法需要调用Message实例上的Dispose方法,这样才能销毁Message 实例拥有的Stream。记住RequestContext是一个抽象类型,因此继承RequestContext的类型可以根据需要提供自己的实现。
注释:与Message类型一样,RequestContext类型明确地实现了IDisposable接口
 
 

双工通道:IDuplexChannel

双工通道支持双工消息交换模式(MEP)。与数据报和请求/应答消息交换模式不同的是,双工消息交换模式运行发送者和接收者自由地发送和接受消息。我们在第3章里曾经看到,双工消息交换模式里的消息通信很像电话通话。在开始通信以前,发送者和接收者必须建立通信上下文环境。在,双工消息交换模式里,发送和接受通道形状是相同的,因此,发送者和接收者实现了相同的接口(假设连个消息参与者都是WCF程序)。因为双工消息交换模式的与生俱来的自由特性,以及接收者和发送公用相同的接口,因此只能通过发起通信来区分哪个是消息的发送者(就像只能通过谁先拨号,来确定谁是打电话的人一样)。

发送和接收接口:IDuplexChannel

IDuplexChannel接口实际上是IInputChannel和IOutputChannel的结合体。如前文所述,IInputChannel是为了实现了数据报消息接收者,而IOutputChannel是为了实现数据报发送者。因为支持双工通信的通道必须能够发送和接受消息,所以逻辑上,IDuplexChannel成员是数据报交换模式里使用的所有成员合并的结果。IDuplexChannel接口的定义如下:
 
InBlock.gif public interface IDuplexChannel : IInputChannel, IOutputChannel, IChannel,
InBlock.gif ICommunicationObject
InBlock.gif{
InBlock.gif}
IDefaultCommunicationTimeouts接口
因为大部分应用程序开发人员都不回直接接触通道,因此通道层必须有一种表示特定操作超时的方法。考虑到通道超时问题的时候,有 4个时间敏感的操作:打开通道、发送消息、接受消息和关闭通道。和通道层的其它功能一样,WCF类型系统包含一个描述超时的接口。System.ServiceModel. IDefaultCommunicationTimeouts,定义如下:
InBlock.gif public  interface IDefaultCommunicationTimeouts { 
InBlock.gif            TimeSpan CloseTimeout { get; } 
InBlock.gif            TimeSpan OpenTimeout { get; } 
InBlock.gif            TimeSpan ReceiveTimeout { get; } 
InBlock.gif            TimeSpan SendTimeout { get; } 
InBlock.gif}
IDefaultCommunicationTimeouts接口里每个成员的作用可以从你名字里推测出来。绑定、通道工厂和通道都实现了这个接口。因为绑定、通道工厂和通道实现了相同的接口,这些类型都可以传递超时到构造链中。例如,一个用户可以在 Binding里指定发送超时属性(Binding提供了setter器)。如果 Binding是消息发送者的一部分,它就可以把超时属性的值通过通道工厂的构造函数传递给通道工厂。同样,通道工厂也可以把超时属性的值传递给通道的构造函数。作用上看,这一系列的传递提供给用户可以通过API指定超时属性的能力,并且这些设置可以作用于通道层上。

ChannelBase类型

所有的自定义通道必须实现公共的状态机,并且暴露GetProperty<T>查询机制,实现一个或者多个通道形状,从通道工厂里接受一个超时设置。 System.ServiceModel.Channels.ChannelBase抽象类型就是这个目的,它确保了所有的通道成员的兼容性。下面代码展示了ChannelBase的类型定义:
 
InBlock.gif public abstract class ChannelBase : CommunicationObject,
InBlock.gif                                                                        IChannel,
InBlock.gif                                                                        ICommunicationObject,
InBlock.gif                                                                        IDefaultCommunicationTimeouts {
InBlock.gif // Constructor with channel factory parameter
InBlock.gif protected ChannelBase(ChannelManagerBase channelManager);
InBlock.gif // IChannel implementation
InBlock.gif public virtual T GetProperty<T>() where T: class;
InBlock.gif // CommunicationObject members
InBlock.gif protected override TimeSpan DefaultCloseTimeout { get; }
InBlock.gif protected override TimeSpan DefaultOpenTimeout { get; }
InBlock.gif protected override void OnClosed();
InBlock.gif protected TimeSpan DefaultReceiveTimeout { get; }
InBlock.gif protected TimeSpan DefaultSendTimeout { get; }
InBlock.gif // IDefaultCommunicationTimeouts implementation
InBlock.gif TimeSpan IDefaultCommunicationTimeouts.CloseTimeout { get; }
InBlock.gif TimeSpan IDefaultCommunicationTimeouts.OpenTimeout { get; }
InBlock.gif TimeSpan IDefaultCommunicationTimeouts.ReceiveTimeout { get; }
InBlock.gif TimeSpan IDefaultCommunicationTimeouts.SendTimeout { get; }
InBlock.gif // reference to channel factory
InBlock.gif protected ChannelManagerBase Manager { get; }
InBlock.gif private ChannelManagerBase channelManager;
InBlock.gif}
ChannelManagerBase的成员表示的是工厂创建通道的方法。第 7章“通道管理器”里会详细介绍这些内容。形状,假设ChannelManagerBase类型一直会从通道工厂里获取超时设置的值。注意ChannelBase里的TimeSpan类型的成员。以Default开头的成员都会从通道工厂里获取超时设置的值,而且这里明确实现了IDefaultCommunicationTimeouts的成员。如下所示:
InBlock.gif protected  override TimeSpan DefaultOpenTimeout { 
InBlock.gif get { 
InBlock.gif      return ((IDefaultCommunicationTimeouts) this.channelManager).OpenTimeout; 
InBlock.gif } 
InBlock.gif
InBlock.gif // delegate to DefaultOpenTimeout property TimeSpan 
InBlock.gifIDefaultCommunicationTimeouts.OpenTimeout { 
InBlock.gif get { 
InBlock.gif         return  this.DefaultOpenTimeout; 
InBlock.gif } 
InBlock.gif}
上面的代码仅仅介绍了通道里open如何实现超时值的传递。close、send和receive方法实现的方式类似。


 本文转自 frankxulei 51CTO博客,原文链接:http://blog.51cto.com/frankxulei/318567 ,如需转载请自行联系原作者




相关文章
|
10月前
|
前端开发
WCF更新服务引用报错的原因之一
WCF更新服务引用报错的原因之一
|
9月前
|
C# 数据安全/隐私保护
c#如何创建WCF服务到发布(SqlServer版已经验证)
c#如何创建WCF服务到发布(SqlServer版已经验证)
38 0
|
9月前
|
安全 数据库连接 数据库
WCF服务创建到发布(SqlServer版)
在本示例开始之前,让我们先来了解一下什么是wcf? wcf有哪些特点? wcf是一个面向服务编程的综合分层架构。该架构的项层为服务模型层。 使用户用最少的时间和精力建立自己的软件产品和外界通信的模型。它使得开发者能够建立一个跨平台的安全、可信赖、事务性的解决方案。且能与已有系统兼容写作。 简单概括就是:一组数据通信的应用程序开发接口。
62 0
|
10月前
Visual Studio 2022 创建 WCF服务 找不到
Visual Studio 2022 创建 WCF服务 找不到