程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> WCF後續之旅(3) WCF Service Mode Layer 的中樞—Dispatcher

WCF後續之旅(3) WCF Service Mode Layer 的中樞—Dispatcher

編輯:關於.NET

在本系列的第一部分、第二部分中,我們對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個核心的對象:ChannelListener、ChannelDispatcher和EndpointDispatcher,和一些輔助的對象。

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:Address、Binding和Contract之外還有重要的屬性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都定義了兩個特殊的屬性:AddressFilter和ContractFilter,它們的類型都是: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的InstanceProvider的ReleaseInstance方法對現有的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:最後作一些資源的釋放和清理工作

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved