WCF后续之旅(3): WCF Service Mode Layer 的中枢—Dispatcher

在本系列的第一部分第二部分中,我们对WCF的channel layer进行了深入的讨论。我们接下来继续讨论WCF的service mode layer。本篇文章着重介绍service 端的ServiceMode。写作此篇文章旨在达到以下两个目的:

  • 希望读者对ServiceMode有一个大致的了解,结合前面介绍的channel layer的相关知识,帮助读者了解WCF的整个实现机制和执行的流程。
  • 介绍ServiceMode涉及到的绝大部分extension point,让读者在具体的项目开发中能够根据实际的需要灵活、自由地对WCF进行扩展。

较之channel layer,ServiceMode要复杂得多,其内部隐藏了太多MS没有公布出来的实现细节。由于到目前为止,还不曾出现过关于此方面详细的官方介绍,以下所有的介绍来源于以下几个方面:

  • 对MSDN的查阅
  • 对相关WCF著作的查阅,比如《Programming WCF Service》、《Inside Microsoft Windows Communication Foundation》等等。
  • 通过Reflector反射出来的代码的理解,本片文章的绝大部分内容来源于此。

所以,对于文中的某些表述,由于个人能力所限,可能有失偏颇。如有不当之处,还望各位朋友及时指正。

1. Dispatcher为我们做了什么?

由于应用WCF的是一个分布式环境,按照所处的环境的不同,可以将ServiceMode分成client端的ServiceMode和service端的ServiceMode。就其实现的复杂度而言,service端的ServiceMode要比client端的复杂很多。对于Service端来讲,WCF的ServiceMode需要解决的是:

  • 如何根据不同的listening URI创建ChannelListener并进行监听;
  • 当request抵达,如何创建适合的Channel接收request message;
  • 如何将Message分发到对应的Endpoint进行处理;
  • 如何进一步将Message分发到对应的service instance
  • 以及如何进一步地分发的具体的service instance的匹配的method call

由于“分发(Dispatch)”是其根本的功能和任务,所以Dispatcher是整个Service端ServiceMode的核心。正如标题所述,Dispatcher是整个WCF service mode layer的中枢,本篇文章讲着重围绕着Dispatcher来展开介绍。

Dispatcher并不是指的某一个对象,而是指完成整个dispatch功能的一组相关对象的总称。这包括3个核心的对象:ChannelListenerChannelDispatcherEndpointDispatcher,和一些辅助的对象。

ChannelListener在本系列的前面两个部分已经进行了详细的介绍,我们知道其主要功能在于:绑定到一个固定的Listening URI,监听来自外界的请求。一旦请求抵达,创建对应的Channel接收Request message。但是我们的业务逻辑定义在一个个的service类中,所以WCF必须提供一种机制通过我们接收到的message去激活对应service instance并调用对应的方法。对于的激活(Activation)包含两种:创建一个新的service instance(PerCall instancing mode)和复用一个已经存在的service Instance(PerSession和Singleton instancing mode)。ChannelDispatcher的核心功能就是提供了这样一种功能(尽管它还提供了其他的有用的功能,为了是内容不至于太散,在这里就不再作相关的介绍)。ChannelDispatcher通常和一个ChannelListener关联,而ChannelListener又对应着一个固定的listening URI。对于一个被host的service来讲,可能定义了不同的listening address,所以一个service一般对应着一到多个ChannelDispatcher。更进一步说,当我们host一个service的时候,WCF会为之创建一个ServiceHostBase对象(ServiceHost或者是你自定义的继承自ServiceHostBase的对象),所以一个ServiceHostBase对象对应一到多个ChannelDispatcher对象

对于接收到的request message,ChannelDispatcher不会自己对其进行处理,而是将其分发到与之相匹配为的EndpointDispatcher上,所以处理message的的绝大部分功能实际上是由EndpointDispatcher来实现的。对于同一个listening address,我们一般会不止一个endpoint,所以一个ChannelDispatcher拥有不止一个EndpointDispatcher。对于EndpointDispatcher来讲,有一个绝对绝对值得特别介绍的是DispatchRumtime。DispatchRumtime和一个特定的EndpointDispatcher匹配,通过定制DispatchRumtime,你可以很容易地按照你具体的需要改变整个service或者某个具体的Operation相关的运行时行为。对于WCF一门重要的课题, WCF extensions来讲,你的绝大部分BehaviorExtesionElment,都是通过具体的Behavior对DispatchRumtime进行定制而实现的

2. WCF Dispatching System的执行流程

有了上面对于ChannelDispatcher和ChannelDispatcher的了解,我们来简单介绍一下WCF的Dispatching System得执行流程:

Step 1:ServiceHost和ServiceDescription的创建

WCF service不能独立地执行,必须Host到一个可执行程序中,可以使一般的managed application、IIS、WAS和Windows service等等。Hosting的工作一般分两个步骤,为service创建ServiceHost打开ServiceHost开始监听请求

注:确实地讲,完成Hosting的不一定是ServiceHost,任何继承自SerivceHostBase的都可以用于service的hosting。ServiceHost继承自ServiceHostBase,我们一般用它来完成Hosting的工作;为了简单,这里的ServiceHost可以看成是任何继承自己ServiceHostBase的Service Host type。

ServiceHost提供了两个重载的constructor:

public ServiceHost(object singletonInstance, params Uri[] baseAddresses); 
public ServiceHost(Type serviceType, params Uri[] baseAddresses);

第一个constructor接收一个service instance,采用Singleton的instancing mode进行Host;而另一个传入service type进行hosting,instancing mode有ServiceBehavior的InstanceContextMode决定。

构造serviceHost,最重要的任务是通过reflection或者解析configuration的方式创建ServiceDescription。这个创建出来的ServiceDescition将是所有后续工作的根本依据。我们来简单了解一下ServiceDescription的属性定义:

public class ServiceDescription
{
// Properties
public KeyedByTypeCollection<IServiceBehavior> Behaviors { get; }
public string ConfigurationName { get; set; }
public ServiceEndpointCollection Endpoints { get; }
public string Name { get; set; }
public string Namespace { get; set; }
public Type ServiceType { get; set; }
}

这里最重要的两个属性是Behaviors 和Endpoints:

  • Behaviors :和service相关的所有service behavior的集合,这里的service behavior仅包含你通过ServiceBehaviorAttribute指定的service behavior,也包含一些自定义的custom service behavior(自定义service behavior在WCF extension中十分常见)。由于该属性的类型是KeyedByTypeCollection<IServiceBehavior> ,所以很容易通过具体的service behavior type得到对应的behavior。
  • Endpoints:service相关联的所有ServiceEndpoint的集合。下面是ServiceEndpoint的属性定义:
public class ServiceEndpoint
{
public EndpointAddress Address { get; set; }
public KeyedByTypeCollection<IEndpointBehavior> Behaviors { get; }
public Binding Binding { get; set; }
public ContractDescription Contract { get; }
public Uri ListenUri { get; set; }
public ListenUriMode ListenUriMode { get; set; }
public string Name { get; set; }
}

除了构成Endpoint的ABC:AddressBindingContract之外还有重要的属性Behaviors,一个包含所有EndpointBehavior的集合。ServiceBehavior、EndpointBehavior、ContractBehavior以及OperationBehavior是我们用户WCF extension的“四大”behavior。这里再额外提一下Contract对应的类型ContractDescription 的定义:

public class ContractDescription
{
public KeyedByTypeCollection<IContractBehavior> Behaviors { get; }
public Type CallbackContractType { get; set; }
public string ConfigurationName { get; set; }
public Type ContractType { get; set; }
public bool HasProtectionLevel { get; }
public string Name { get; set; }
public string Namespace { get; set; }
public OperationDescriptionCollection Operations { get; }
public ProtectionLevel ProtectionLevel { get; set; }
public SessionMode SessionMode { get; set; }
}
  • Behaviors :所有ContractBehavior的集合。
  • ContractType :Contract的类型,返回标注了ServiceContractAttribute的class对应的type,可能是interface也可能是class。
  • Operations :Contract中所有Operation的描述,每个元素与应用了OperationAttribute的method一一匹配。 OperationDescriptionCollection 的定义也相当重要,至于具体的定义,可以参考MSDN,限于篇幅,在这里就不多作介绍了。
  • SessionMode:Session的模式:Allowed、Required、NotAllowed。

注:当通过实例化ServiceHost创建的ServiceDescription可以通过SeriviceHost对象的Description属性获得,你还可以代码的方式对其进行添加、删除和修改。

Step 2:ServiceHost.Open()和Channel Listener & ChannelDipatcher的创建

当调用ServiceHost instance的Open方法后。WCF遍历ServiceHost的Endpoints属性中的每个ServiceEndpoint,对于每个不同的不同的ListenUri和ListenUriMode的不同,通过ServiceEndpoint对象的Binding创建ChennelListener(关于通过Binding创建ChannelListener本系列的第一部分作了详细的介绍)。举个例子,假设一个ServiceHost有4个Endpoint:

Endpoint Listen URI Listen URI Mode
endpoint I http://artech/serviceA Explicit
endpoint II http://artech/serviceA Explicit
endpoint III http://artech/serviceA Unique
endpoint IV http://artech/serviceB Explicit

注:上面的例子仅仅是用于说明endpoint和Channel Listener的关系,和真实的情况有些出入。

那么此service最终的listening address包含3个,endpoint I和endpointII具有一样的listening address,其他的都不一样。那么会分别利用3个ServiceEndpoint的Binding属性获取对应的Binding对象,调用Binding对象的BuildChannelListener方法创建3个ChannelListener。然后将此ChannelListener传入ChannelDispatcher的构造函数,创建3个对应的ChannelDispatcher对象。对于每个ChannelDispatcher,由于有不止一个的Endpoint,比如其中一个ChannelDispatcher就具有endpoint I和endpoint II两个ServiceEndpoint,所以一个ChannelDispatcher可能有不止一个EndpointDispatcher。在创建EndpointDispatcher的同时,另一个非常重要的对象一并被创建出来:DispatchRuntime。DispatchRuntime反应了绝大部分ChannelDispatcher和EndpointDispatcher运行时的信息。通过对DispatchRuntime的定制,你可以很方面地改变整个Dispatching的行为。

Step 3:请求接听和消息接收

ChannelDispatcher创建之后,其Open方法会调用。随之ChannelDispatcher对应的ChannelListener会被打开,并绑定到对应的listening URI进行监听来自外界的request。当request抵达并被检测到,ChannelListener会调用AcceptChannel方法创建一个适合的Channel,该Channel将用作接收request message。

Step 4:获取匹配的EndpointDispatcher,并分发request message

当request message被成功接收,ChannelDispatcher本身并不负责处理该message,而是将其分发到匹配的EndpointDispatcher。我们在上面已经说过了,一个ChannelDispatcher可以同时拥有不止一个EndpointDispatcher,但是最终接收并处理message的EndpointDispatcher却只能有一个。ChannelDispatcher如何选择所需的EndpointDispatcher呢?

具体实现是这样的:每个EndpointDispatcher都定义了两个特殊的属性:AddressFilterContractFilter,它们的类型都是:System.ServiceModel.Dispatcher.MessageFilter

public abstract class MessageFilter
{
// Methods
protected MessageFilter();
protected internal virtual IMessageFilterTable<FilterData> CreateFilterTable<FilterData>();
public abstract bool Match(Message message);
public abstract bool Match(MessageBuffer buffer);
}

MessageFilter定义了两个Match重载,所以子类实现该重载实现自定义的匹配规则。在具体实现的时候,会解析request message或者MessageBuffer (message 的memory buffer表示)(一般是message header),来判断该request是否和对应的EndpointDispatcher相互匹配。

WCF为我们提供了一下6类Message Filter:

  • System.ServiceModel.Dispatcher.ActionMessageFilter:通过message Action header进行匹配。
  • System.ServiceModel.Dispatcher.MatchAllMessageFilter:匹配所有的message,也就是直接返回true。
  • System.ServiceModel.Dispatcher.EndpointAddressMessageFilter:根据message的To header 进行匹配。
  • System.ServiceModel.Dispatcher.MatchNoneMessageFilter:不会和任何的message匹配,也就是直接返回false。
  • System.ServiceModel.Dispatcher.PrefixEndpointAddressMessageFilter:和EndpointAddressMessageFilter相似,不过这次匹配的不是整个message的To header ,而是To header 的前缀
  • System.ServiceModel.Dispatcher.XPathMessageFilter:基于Xpath expression的匹配方式。

那么EndpointDispatcher的ContractFilter和AddressFilter采用的又是那种类型的MessageFilter呢?通过Reflector看看EndpointDispatcher的构造函数就会知道,AddressFilter采用的是EndpointAddressMessageFilter,而ContractFilter采用的则是MatchAllMessageFilter。也就是说,当ChannelDispather在为接收到的request message选择合适的EndpointDispatcher的时候,会根据message To header上的address进行匹配。

你也许会问,如何有不止一个EndpointDispatcher满足条件怎么办呢?答案是ChannelDispatcher会选择一个Filter优先级最高的一个,该优先级由EndpointDispather的FilterPriority属性来决定

Step 5:InstanceContext的获取

在WCF infrastructure中, InstanceContext是以一个很重要的概念。InstanceContext是什么呢?简言之,InstanceContext就是对service instance的封装(service instance wrapper),对于每一个service instance来讲,WCF都会通过一些context信息对其进行包装。这些contextual information存在的目的在于让不同的request关联到对应的service instance上。对于不同的Instancing Mode(PerCall、PerSession和Singleton),我们往往具有不同的InstanceContext。而对于PerSession方式的instancing mode,InstanceContext显得尤为重要,原因很简单,我们必须保证来自同一个Session的request被分发到同一个service instance,不然很难维护其session的信息

InstanceContext的获取通过InstanceContextProvider来实现。在WCF中所有的InstanceContextProvider均实现了System.ServiceModel.Dispatcher.IInstanceContextProvider interface。

public interface IInstanceContextProvider
{
// Methods
InstanceContext GetExistingInstanceContext(Message message, IContextChannel channel);
void InitializeInstanceContext(InstanceContext instanceContext, Message message, IContextChannel channel);
bool IsIdle(InstanceContext instanceContext);
void NotifyIdle(InstanceContextIdleCallback callback, InstanceContext instanceContext);
}
  • GetExistingInstanceContext:当message由ChannelDispather交付到EndpointDispatcher的时候,该方法会被调用,试图获取一个已经存在的InstanceContext。比如PerSession模式下,如何sesssion已经开始,那个会或其绑定到当前session的InstanceContext,否则return null;对于Singleton模式,由于使用一个service instance来处理所有的request,所以一旦service instance被创建,后续的request都将返回同一个InstanceContext,否则return null;而对于PerCall来说,由于对于每个request来说,都需要创建一个新的service instance来处理,所以它永远是return null。
  • InitializeInstanceContext:如何GetExistingInstanceContext返回null,将通过这个方法来创建和初始化一个新的InstanceContext.
  • IsIdle:当所有的InstanceContext操作完成以后,该方法会被调用,返回的bool类型的结果将用作是否对InstanceContext进行清理和回收的依据。如何你不希望对创建的InstanceContext进行回收,那么你可以将此方法返回为false。比如Singleton和PerSession模式下就直接return false;而PerCall则return true。
  • NotifyIdle:当对InstanceContext进行真正的清理和回收时,此方法会被回调。

具体的做法是:通过DispatchRuntime的InstanceContextProvider属性获取与instancing mode相匹配的InstanceContextProvider对象,调用GetExistingInstanceContext方法获取existing InstanceContext。

和3种instancing mode向匹配, WCF定义了3种InstanceContextProvider:

  • System.ServiceModel.Dispatcher.PerCallInstanceContextProvider
  • System.ServiceModel.Dispatcher.PerSessionInstanceContextProvider
  • System.ServiceModel.Dispatcher.SingletonInstanceContextProvider

 Step 6:选择DispatchOperation

每个request message是基于某个固定operation的,因为它反映的是基于某个service operation的调用。Dispatching system通过DispatchOperation来表示一个具体的service operation。DispatchRuntime定义了一个Operations属性,该属性表示所有DispatchOperation的集合。现在的问题是如何从众多的DispatchOperation选择一个和当前request message向匹配的DispatchOperation。

DispatchOperation的选择通过DispatchRuntime的另一个属性OperationSelector来完成。OperationSelector是一个System.ServiceModel.Dispatcher.IDispatchOperationSelector对象:

public interface IDispatchOperationSelector
{
string SelectOperation(ref Message message);
}

对DispatchOperation的选择既是同调用DispatchOperationSelector的SelectOperation方法,通过解析request message得到最终operation的名称,根据获得的这个名称在DispatchRuntime的Operations集合中找到对应的DispatchOperation,并将request message分发个该DispatchOperation

WCF只定义了一个实现了IDispatchOperationSelector的OperationSelector:System.ServiceModel.Channels.OperationSelector,一般情况下,他返回的是message Action header的值。

Step 7:初始化当前的OperationContext

WCF通过System.ServiceModel.OperationContext来表示Operation相关的Context信息。Step 7会设置current OperationContext(OperationContext.Current)。

Step 8: 验证Addressing信息

如果EndpointDispatcher的ManualAddressing属性为false(表示采用默认的WS-Addressing方式进行Addressing),会按照WS-Addressing的标准对Addressing相关的message header进行检验。将的具体点,会验证message header中的Reply address、FaultTo address和From address。

Step 9:对request message进行检验或修改

在DispatchRuntime中有一个MessageInspectors集合属性,其中的每一个元素是一个个的MessageInspector对象。MessageInspector存在于client端和service端,client端较做ClientMessageInspector,实现System.ServiceModel.Dispatcher.IClientMessageInspector interface,而service端叫做 DispatchMessageInspector, 实现了System.ServiceModel.Dispatcher.IDispatchMessageInspector interface。DispatchMessageInspector允许你在request message交付到具体的DispatchOperation付诸执行之前或者reply message返回client之前对incoming message/outgoing message进行检验、修改或者其他一些基于message的操作。

IDispatchMessageInspector的定义很简单:

public interface IDispatchMessageInspector
{
// Methods
object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext);
void BeforeSendReply(ref Message reply, object correlationState);
}

具体的做法是:遍历DispatchRuntime的MessageInspectors集合的每一个IDispatchMessageInspector对象,调用AfterReceiveRequest方法对request message进行检验或者修改。

Step 10:对现有的service instance进行释放和回收

如果DispatchOperation的ReleaseInstanceBeforeCall 为true, 调用DispatchRuntime的InstanceProviderReleaseInstance方法对现有的Service Instance(如果存在)进行释放和清理。

顾名思义,InstanceProvider就是用于创建或者提供service instance的。除了提供service instance的创建者或者提供者的身份外,InstanceProvider还用于service instance的释放和回收。所有的IntanceProvider实现了System.ServiceModel.Dispatcher.IInstanceProvider interface。

public interface IInstanceProvider
{
// Methods
object GetInstance(InstanceContext instanceContext);
object GetInstance(InstanceContext instanceContext, Message message);
void ReleaseInstance(InstanceContext instanceContext, object instance);
}

如果InstanceProvider对应的DispatchOperation.ReleaseInstanceBeforeCall 为true的话,IntanceProvider将通过DispatchRuntime的InstanceProvide属性提取出来,通过调用ReleaseInstance()方法释放掉现有的instance。

Step 11:获取或者创建service instance

service instance的获取或者创建是通过InstanceProvider的GetInstance()方法完成。

Step 12: 对当前线程的Context进行初始化

在真正执行我们的service operation方法的时候,可以会依赖一些当前线程的Context信息。WCF dispatching system 通过一个叫做CallContextInitializer的对象进行thread context的初始化或者清理工作。每个DispatchOperation都具有一个CallContextInitializers的集合属性,其中每个元素为与此operation相关的CallContextInitializer对象。

提到CallContextInitializer,我想有一部分人会马上想到System.Runtime.Remoting.Messaging.CallContext。CallContext为我们创建基于当前线程的Ambient context,提供了便利。通过CallConext,我们和容易地将一些contextual information至于TLS(Thread Local Storage)中。

与之相似的,DispatchOperation的CallContextInitializers提供了一个CallContextInitializer的集合,这些CallContextInitializer可以帮助我们对TLS进行初始化和释放回收的工作。比如在某个service 方法被真正之前,我们希望设置一些Context的数据,这些数据可能使业务有关,但大部分是和具体的业务逻辑没有关系的,比如一些Auditing的数据。在方式执行完成后,对这些context数据进行清理和回收。 WCF下的所有CallContextInitializer实现了System.ServiceModel.Dispatcher.ICallContextInitializer interface:

public interface ICallContextInitializer
{
// Methods
void AfterInvoke(object correlationState);
object BeforeInvoke(InstanceContext instanceContext, IClientChannel channel, Message message);
}

具体的做法是:遍历当前DispatchOperation的CallContextInitializers集合属性的每个CallContextInitializer对象,调用其BeforeInvoke()方法进行thread context的初始化工作。

Step 13:反序列化输入参数列表

对整个WCF infrastructure,我们可以将其分成两个世界,其中一个是基于message的世界;而另一个则是object的世界。对于前者来讲,所有的数据通过message进行封装,后者则同一个个具体的object来呈现。要实现具体的service功能,毫无疑问,需要调用具体的方法,传入具体的参数,而这些输入参数是一个个的对象,方法执行完成生成的结果也是一个个的对象。但是我们最初接受的request确实一个message,方法执行的参数也一infoset的形式封装在message中;我们最重终生成的结果也不能以object的形式返回来client。所以我们需要一个这样的中介:将输入参数从message中提出,并转化成object;同是将返回值从object形式转化成message。这样的中介就是:MessageFormatter。

和MessageInspector一样,client端和service的Formatter是不同的。client端叫做ClientMessageFormatter ,实现了System.ServiceModel.Dispatcher.IClientMessageFormatter interface;service端叫做DispatchMessageFormatter, 实现了System.ServiceModel.Dispatcher.IDispatchMessageFormatter interface.

public interface IDispatchMessageFormatter
{
// Methods
void DeserializeRequest(Message message, object[] parameters);
Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result);
}

DispatchOperation中定义了一个DeserializeRequest的属性,用于判断是否需要反序列化request message声称一个输入参数对象列表。为什么需要有此属性呢?因为有些情况下,我们是不需要对此反序列化步骤的,比如:我们的Operation直接将message作为参数。

在Step 13中,先根据DispatchOperation的DeserializeRequest属性判断是否需要进行凡序列化操作,如何需要,则通过DispatchOperation的Formatter属性获取IDispatchMessageInspector对象,调用DeserializeRequest解析request message,通过反序列化声称一个输入参数的对象数组。

Step 14:对输入参数进行验证

Security有这样的一个原则:不能完全信任来自用户或者访问者的输入。就像Asp.NET通过一个个validator control来保证用户输入的合法性一样,WCF也需要有这样的机制。而这样的功能是通过ClientOperation或者DispatchOperation的ParameterInspectors集合实现的。

当执行具体的service method之前,会遍历DispatchOperation ParameterInspectors集合中的每个ParameterInspector,并调用BeforeCall对输入参数进行验证;而当service method被真正执行后,会生成返回值或者输出参数,在这个时候对ParameterInspectors的遍历再次进行,不果这次调用的是AfterCall方法,AfterCall方法旨在对返回值或者输出参数进行验证。

所有的ParameterInspector均实现了System.ServiceModel.Dispatcher.IParameterInspector interface。

public interface IParameterInspector
{
// Methods
void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState);
object BeforeCall(string operationName, object[] inputs);
}

注: 大家可能已经注意到了,BeforeCall有一个返回值。这个返回值得目的在于同AfterCall进行批评。在调用AfterCall是,这个返回值将会传入第三个参数:correlationState。

在Step 14中,会遍历DispatchOperation的ParameterInspectors集合属性中的每一个

IParameterInspector对象,调用其BeforeCall对输入参数进行验证。

Step 15:执行service operation方法

知道这一步,整个的service operation method才真正被执行。具体的做法是通过DispatchOperation的Invoker属性得到一个System.ServiceModel.Dispatcher.IOperationInvoker的对象,IOperationInvoker得定义如下:

public interface IOperationInvoker
{
// Methods
object[] AllocateInputs();
object Invoke(object instance, object[] inputs, out object[] outputs);
IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state);
object InvokeEnd(object instance, out object[] outputs, IAsyncResult result);

// Properties
bool IsSynchronous { get; }
}

如果以同步的方式执行操作的话则执行Invoke() 方法,倘若以异步的方式则调用InvokeBegin/InvokeEnd方法。具体的实现很简单,我们已同步的方式为例:首先通过当前表示当前Operation描述信息的OperationDescription对象,通过该对象的SyncMethod属性得到具体的方法名称,通过service type得到MethodInfo对象,然后调用MethodInfo对象的Invoke方法,传入service instance对象和输入参数。执行的结果会生成返回值或者输出参数列表。

Step 16:对返回值或输出参数进行检验

在Step 14,上面我们提到通过DispatchOperation的PameteterInpectors集合中的PameteterInpector对输入参数进行验证,现在我们利用它来对返回值或者输出参数进行验证。

具体的做法是,遍历PameteterInpectors集合中的每个PameteterInpector对象,调用AfterCall()方法,从而实现对返回值和输出参数的验证。

Step 17: 序列化返回值或者输出参数

正如要调用具体的service method,需要将request message反序列化生成一串输入参数对象列表(Step 13),同样地,要将返回值或者输出参数返回给访问者,需要将它们进行序列化。

具体的做法是:通过DispatchOperation的SerializeReply属性判断是否需要对返回值和输出参数进行序列化,如何需要,则通过Formatter属性获取具体的IDispatchMessageFormatter对象,调用SerializeReply方法对返回值和输出参数进行序列化。

Step 18:清理thread context

在Step 12 中,我们通过DispatchOperation的CallContextInitializers集合实现对当前线程的context进行初始化工作,与之相对地,当service operation执行完成,我们同样可以通过他们对thread context进行清理操作。

具体的做法是:遍历CallContextInitializers中的每一个CallContextInitializer,调用其AfterCall()方法实现对thread context的清理和释放。

Step 19:Error Handling

无论是对于具体的项目开发也好,还是对Framework的开发也罢,对异常、错误的处理都是必须的。在WCF,通过ErrorHandler对象,你可以很容易地实现对异常的处理。ChannelDispatcher中将一个ErrorHandler的集合定义在ErrorHandlers属性中。当出现exception的时候,会遍历这个ErrorHandlers集合中的每个ErrorHandler。调用HandleError方法和ProvideFault方法。

所有的ErrorHandler都实现了System.ServiceModel.Dispatcher.IErrorHandler interface:

public interface IErrorHandler
{
// Methods
bool HandleError(Exception error);
void ProvideFault(Exception error, MessageVersion version, ref Message fault);
}

具体的做法是:遍历ChannelDispatcher的ErrorHandlers集合中的每一个ErrorHandler,调用其HandleError方法,如何其中有一个返回true,将推出迭代。

Step 20:最后作一些资源的释放和清理工作 


WCF后续之旅: 
WCF后续之旅(1): WCF是如何通过Binding进行通信的 
WCF后续之旅(2): 如何对Channel Layer进行扩展——创建自定义Channel 
WCF后续之旅(3): WCF Service Mode Layer 的中枢—Dispatcher 
WCF后续之旅(4):WCF Extension Point 概览 
WCF后续之旅(5): 通过WCF Extension实现Localization 
WCF后续之旅(6): 通过WCF Extension实现Context信息的传递 
WCF后续之旅(7):通过WCF Extension实现和Enterprise Library Unity Container的集成 
WCF后续之旅(8):通过WCF Extension 实现与MS Enterprise Library Policy Injection Application Block 的集成 
WCF后续之旅(9):通过WCF的双向通信实现Session管理[Part I] 
WCF后续之旅(9): 通过WCF双向通信实现Session管理[Part II] 
WCF后续之旅(10): 通过WCF Extension实现以对象池的方式创建Service Instance 
WCF后续之旅(11): 关于并发、回调的线程关联性(Thread Affinity) 
WCF后续之旅(12): 线程关联性(Thread Affinity)对WCF并发访问的影响 
WCF后续之旅(13): 创建一个简单的WCF SOAP Message拦截、转发工具[上篇] 
WCF后续之旅(13):创建一个简单的SOAP Message拦截、转发工具[下篇] 
WCF后续之旅(14):TCP端口共享 
WCF后续之旅(15): 逻辑地址和物理地址 
WCF后续之旅(16): 消息是如何分发到Endpoint的--消息筛选(Message Filter) 

WCF后续之旅(17):通过tcpTracer进行消息的路由 

posted @ 2008-07-15 09:14  Artech  阅读(13637)  评论(72编辑  收藏  举报