閱讀801 返回首頁    go 微軟 go windows


WCF後續之旅(4):WCF Extension Point 概覽

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模式下,如果session已經開始,那個會返回綁定到當前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。比如:SingletonPerSession模式下就直接return false;而PerCall則return true。
  • NotifyIdle:當對InstanceContext進行真正的清理和回收時,此方法會被回調。

注:當你自定義InstanceContextProvider的時候,一般繼承base class:System.ServiceModel.Dispatcher.InstanceContextProviderBase而不是完全實現System.ServiceModel.Dispatcher.IInstanceContextProvider interface。

具體實現是這樣的:每個EndpointDispatcher都定義了兩個特殊的屬性:AddressFilterContractFilter,它們的類型繼承了abstract class: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);
}

  • System.ServiceModel.Dispatcher.ActionMessageFilter:通過message Action header進行匹配。
  • System.ServiceModel.Dispatcher.EndpointAddressMessageFilter:根據message的To header 進行匹配。
  • System.ServiceModel.Dispatcher.PrefixEndpointAddressMessageFilter:和EndpointAddressMessageFilter相似,不過這次匹配的不是整個message的To header ,而是To header 的前綴。

那麼EndpointDispatcher的ContractFilter和AddressFilter采用的又是那種類型的MessageFilter呢?通過Reflector看看EndpointDispatcher的構造函數就會知道,AddressFilter采用的是EndpointAddressMessageFilter,而ContractFilter采用的則是MatchAllMessageFilter。也就是說,當ChannelDispather在為接收到的request message選擇合適的EndpointDispatcher的時候,會根據message To header上的address進行匹配。

你也許會問,如何有不止一個EndpointDispatcher滿足匹配條件怎麼辦呢?答案是:ChannelDispatcher會選擇一個Filter優先級最高的一個,該優先級由EndpointDispather的FilterPriority屬性來決定。

如何你想改變這種默認的filter方式,你可以通過你自定義的behavior,來改變EndpointDispatcher的這兩個Filter:AddressFilter和ContractFilter。你或許會懷疑是否有這樣做的必要,那麼我給你一個具體的例子:我們都知道我們有兩種傳統的Web request方式:Get和Post,前者根據query string,後者基於form。在實際的項目中,你可能會使用到形如Get的方式來訪問WCF service,這在Ajax的應用中應該有這樣的需求,那麼你就不能以現有的Filter方式找到所需的EndpointDispather,這時候,你需要一個特殊的AddressFilter,一個基於解析query string的filter.

在client端和service端都有自己的MessageInspector,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的操作;

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

在實際的項目開發中,MessageInspector使用相當廣範,比如:我們可以定義自己的MessageInspector實現Logging功能;或者在Client端通過ClientMessageInspector添加一些與業務無關的context信息,並在service通過DispatchMessageInspector將其取出。在本係列後續的部分我將給你一個application context propagation的具體應用。

顧名思義,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()方法釋放掉現有的service instance。

提到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);
}

再給大家介紹一個使用CallContextInitializer的場景。假設有一個service專門提供message,考慮到Localization,當客戶訪問你的service獲取某項message entry的時候,你希望根據該client的當前culture/UI culture返回具體的message。但是你不希望將這個culture作為API的一部分。這樣你就可以這樣做:在client端,通過自定義ClientMessageInspector將當前的Culture附加到outgoing message的header中。在service端,通過一個自定義的CallContextInitializer,將culture的值從incoming message header取出,並用這個culture設置當前線程的Culture和UICulture。那麼message的獲取隻需要考慮當前線程的Culture就可以了。我將在本係列後續的文章介紹這個應用。

6 、自定義MessageFormatter(Step 13 & Step 17)

對整個WCF infrastructure,我們可以將其分成兩個世界,其中一個是基於message的世界;而另一個則是object的世界。對於前者來講,所有的數據通過message進行封裝,後者則同一個個具體的object來呈現。要實現具體的service功能,毫無疑問,需要調用具體的方法,傳入具體的參數,而這些輸入參數是一個個的對象,方法執行完成生成的結果也是一個個的對象。但是我們最初接受的request確實一個message,方法執行的參數也一XML 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.IDispatchMessageInspector interface:

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

public interface IClientMessageFormatter
{
// Methods
object DeserializeReply(Message message, object[] parameters);
Message SerializeRequest(MessageVersion messageVersion, object[] parameters);
}

運行時真正使用到的MessageFormatter通過ClientOperation和DispatchOperation的Formatter屬性指定。WCF提供的MessageFormatter都是基於DataContract serializer或者XML serializer的。如何現有的這些不能滿足你的需求,你完全可以創建自定義的MessageFormatter,然後通過behavior將其賦值到具體的ClientOperation和DispatchOperation上。

 

注:並非所有的情況下都需要MessageFormamter來幫助我們從事format的工作。我們知道我們的API可以是基於Message對象的,也就是說我們的輸入可以使一個message,返回值也可以是一個具體的message。這種情況下,我們是不需要MessageFormatter的。WCF實際上是通過ClientOperation或者DispatchOperation的SerializeRequest/DeserializeReply和DeserializeRequest/SerializeReply屬性來判斷是否需要對參數或者返回值進行format的

7、 自定義ParameterInspector(Step14 & Step 16)

Security有這樣的一個原則:不能完全信任來自用戶或者訪問者的輸入。就像Asp.NET通過一個個validator control來保證用戶輸入的合法性一樣,WCF也需要有這樣的機製。而這樣的功能是通過ClientOperation或者DispatchOperation的ParameterInspectors集合實現的

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

注: 大家可能已經注意到了,BeforeCall有一個返回值。這個返回值得目的在於同AfterCall進行批評。在調用AfterCall是,這個返回值將會傳入第三個參數:correlationState。

通過自定義ParameterInspector對WCF進行擴展的最典型的應用莫過於Enterprise Library Validation Application Block和WCF的集成,我將在本係列後續的文章對此作介紹。

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

通過自定義ErrorHandler實現對WCF的擴展的典型應用莫過於Enterprise Library Exception Handling Application與WCF的集成

 

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進行消息的路由


作者:蔣金楠
微信公眾賬號:大內老A
微博:www.weibo.com/artech
如果你想及時得到個人撰寫文章以及著作的消息推送,或者想看看個人推薦的技術資料,可以掃描左邊二維碼(或者長按識別二維碼)關注個人公眾號(原來公眾帳號蔣金楠的自媒體將會停用)。
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁麵明顯位置給出原文連接,否則保留追究法律責任的權利。
原文鏈接

最後更新:2017-10-30 17:05:04

  上一篇:go  WCF後續之旅(3): WCF Service Mode Layer 的中樞—Dispatcher
  下一篇:go  WCF後續之旅(5): 通過WCF Extension實現Localization