上篇介紹透過組態檔自訂 ChannelSinkProvider 將 ChannelSink 加入 Pipeline,但只能操作已序列化的資料流。若要攔截原始訊息物件 (修改參數/回傳值、前後置邏輯),需在格式化器前注入 MessageSink,這正是 AOP 核心目標。
IClientChannelSinkProvider 回傳 IClientChannelSink,但 SoapClientFormatterSink 同時實作 IMessageSink 與 IClientChannelSink (後者方法拋 NotSupportedException),證明可「偽裝」成 ChannelSink 實為 MessageSink。
public class CustomMessageSink : IMessageSink, IClientChannelSink
{
public IMessage SyncProcessMessage(IMessage msg)
{
((IMethodMessage)msg).LogicalCallContext.SetData("MyName", "idior");
return _nextSink.SyncProcessMessage(msg);
}
// IClientChannelSink 方法全拋 NotSupportedException
}
Pipeline 非單一鏈表,而是抽象概念。RealProxy 內部維護 IClientChannelSink 鏈表 (透過 Next 屬性),首節點存於 _identity.ChannelSink。首次呼叫時 RemotingProxy.InternalInvoke 呼叫 RemotingServices.CreateEnvoyAndChannelSinks 建立 EnvoySink 鏈與 ChannelSink 鏈,後者首元素型別為 IMessageSink,最終經 SetEnvoyAndChannelSinks 存入 _identity._channelSink。
internal virtual IMessage InternalInvoke(IMethodCallMessage req, bool useDispatch, int callType)
{
if (identity.ChannelSink == null)
{
IMessageSink envoySink, channelSink;
RemotingServices.CreateEnvoyAndChannelSinks(..., out envoySink, out channelSink);
RemotingServices.SetEnvoyAndChannelSinks(identity, envoySink, channelSink);
}
// 呼叫 CallProcessMessage(identity.ChannelSink, req, ...) 其中 ChannelSink 型別為 IMessageSink
}
CallProcessMessage 參數為 IMessageSink,故插入 ChannelSink 鏈首的 MessageSink 會被正確呼叫 SyncProcessMessage。透過實作 IClientChannelSinkProvider 並偽裝成 IClientChannelSink,可將自訂 MessageSink 注入客戶端 Pipeline 格式化器前,實現訊息層級攔截與修改。下篇將探討同一 AppDomain 內跨 Context 呼叫的攔截機制 (涉及更多 Sink 類型)。