微软家的东西大多是基于消息(Message)和上下文(Context)实现的。所以总会有一个Manager的调度器存在负责传递消息和上下文。本文主要目的是通过编程实现获取接口调用时的参数,在前文中是通过 RequestContext 实现的,在这里则对WCF功能进行扩展。
参考文档:
最重要的架构图

这个架构图非常清晰的描述了这个 rpc 消息在 WCF 内部是怎么被调度的。(然而实际上我在写扩展的时候还是靠看 .net 源代码的
项目代码Demo:https://github.com/HDRorz/WcfServiceDemo
翻阅了 System.Servicemodel.Dispatcher 命名空间下大部分东西,发现 IOperationInvoker 是真正接口方法调用前最后一个调度器,是符合我们需求的。查看源代码,IOperationInvoker 是 DispatchOperation 的子属性,DispatchOperation 是DispatchRuntime 的子属性和 IOperationBehavior 的 ApplyDispatchBehavior 方法参数。按照使用行为配置和扩展运行时上的描述。只有 IServiceBehavior 和 IEndpointBehavior 可以在配置文件中应用扩展。(不然可以继承整个 ServiceHostBase,那就是重写WCF了)
经过实验,发现 IEndpointBehavior 的 ApplyDispatchBehavior 方法可以通过参数 endpointDispatcher 获取到 DispatchRuntime。那么我就这样实现了一个 VedaEndpointExtension 和 SyncNadleehOperationInvoker。赋值 DispatchRuntime.Operations 的 Invoker 属性。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
namespace WcfExtensions
{
/// <summary>
/// 吠陀终结点扩展
/// </summary>
public class VedaEndpointBehavior : IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
/// <summary>
/// 只有在客户端时才会被调用
/// </summary>
/// <param name="endpoint"></param>
/// <param name="clientRuntime"></param>
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
}
/// <summary>
/// 增加调度器扩展
/// </summary>
/// <param name="endpoint"></param>
/// <param name="endpointDispatcher"></param>
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
endpointDispatcher.DispatchRuntime.ChannelDispatcher.ErrorHandlers.Add(new ExiaErrorHandler());
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new DynamesMessageInspector());
foreach (var operation in endpointDispatcher.DispatchRuntime.Operations)
{
//原为null。这里设置没有效果,因为实际调用时并没有使用这里的设置
operation.Invoker = new SyncNadleehOperationInvoker(endpoint.Contract.ContractType, operation.Name);
operation.ParameterInspectors.Add(new AstraeaParameterInspector());
}
}
public void Validate(ServiceEndpoint endpoint)
{
}
}
}
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.ServiceModel.Dispatcher;
using System.Text;
using WcfExtensions.Util;
namespace WcfExtensions
{
/// <summary>
/// 娜德雷同步方法调用者
/// 扩展软爹内部的SyncMethodInvoker
/// 在方法调用前后插入自定义的操作
/// </summary>
public class SyncNadleehOperationInvoker : IOperationInvoker
{
private IOperationInvoker innerInvoker = null;
private MethodInfo method;
private string methodName;
private int inputParameterCount;
private int outputParameterCount;
private Type type;
public SyncNadleehOperationInvoker(Type type, string methodName)
{
this.type = type;
this.methodName = methodName;
this.method = this.type.GetMethod(this.methodName);
GetSyncMethodInvoker(type, methodName);
inputParameterCount = method.GetParameters().Count(e => e.IsIn);
outputParameterCount = method.GetParameters().Count(e => e.IsOut);
BeforerInvokerHandles.Add(BeforeMethodInvoke.SetStopWatch);
BeforerInvokerHandles.Add(BeforeMethodInvoke.TokenValid);
AfterInvokerHandles.Add(AfterMethodInvoke.OperatingLog);
}
public bool IsSynchronous
{
get
{
return true;
}
}
public MethodInfo Method
{
get
{
return this.method;
}
}
public string MethodName
{
get
{
if (this.methodName == null)
{
this.methodName = this.method.Name;
}
return this.methodName;
}
}
/// <summary>
/// 方法调用前的自定义操作列表
/// </summary>
private List<BeforeMethodInvokeDelegate> BeforerInvokerHandles = new List<BeforeMethodInvokeDelegate>();
/// <summary>
/// 方法调用后的自定义操作列表
/// </summary>
private List<AfterMethodInvokeDelegate> AfterInvokerHandles = new List<AfterMethodInvokeDelegate>();
/// <summary>
/// 获取软爹内部的SyncMethodInvoker
/// </summary>
/// <param name="type"></param>
/// <param name="methodName"></param>
private void GetSyncMethodInvoker(Type type, string methodName)
{
Type innerInvokerType = typeof(DispatchRuntime).Assembly.GetType("System.ServiceModel.Dispatcher.SyncMethodInvoker");
var constructorMethod = innerInvokerType.GetConstructor(new Type[2] { typeof(Type), typeof(String) });
innerInvoker = (IOperationInvoker)constructorMethod.Invoke(new object[2] { type, methodName });
}
public object[] AllocateInputs()
{
return innerInvoker.AllocateInputs();
}
/// <summary>
/// 在方法调用前后插入自定义的操作
/// </summary>
/// <param name="instance"></param>
/// <param name="inputs"></param>
/// <param name="outputs"></param>
/// <returns></returns>
public object Invoke(object instance, object[] inputs, out object[] outputs)
{
bool ifBreak = false;
object ret = null;
ConcurrentDictionary<string, object> invokeContext = new ConcurrentDictionary<string, object>();
foreach (var beforeInvoke in BeforerInvokerHandles)
{
var temp = beforeInvoke.Invoke(invokeContext, method, instance, inputs, out ifBreak);
if (ifBreak)
{
ret = temp;
break;
}
}
if (!ifBreak)
{
ret = innerInvoker.Invoke(instance, inputs, out outputs); ;
}
else
{
outputs = new object[outputParameterCount];
}
foreach (var afterInvoke in AfterInvokerHandles)
{
afterInvoke.Invoke(invokeContext, method, instance, inputs, outputs, ret);
}
return ret;
}
public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
{
throw new NotImplementedException();
}
public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
{
throw new NotImplementedException();
}
}
}
然后新增扩展配置节点 VedaEndpointBehaviorExtensionElement,并加到配置文件中。
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel.Configuration;
using System.Text;
namespace WcfExtensions.Element
{
public class VedaEndpointBehaviorExtensionElement : BehaviorExtensionElement
{
public override Type BehaviorType
{
get { return typeof(VedaEndpointBehavior); }
}
protected override object CreateBehavior()
{
return new VedaEndpointBehavior();
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.serviceModel>
<services>
<service name="WcfServiceLibrary1.Service1">
<endpoint address="" behaviorConfiguration="restfulBehavior" binding="webHttpBinding" bindingConfiguration="" contract="WcfServiceLibrary1.IService1">
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
<host>
<baseAddresses>
<add baseAddress="http://localhost:8733/Service1/"/>
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="restfulBehavior">
<webHttp/>
<WcfExtension/>
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<extensions>
<behaviorExtensions>
<add name="WcfExtension" type="WcfExtensions.Element.VedaEndpointBehaviorExtensionElement, WcfExtensions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</behaviorExtensions>
</extensions>
</system.serviceModel>
</configuration>
然而这样一通操作后,测试时发现这个 Invoker 没有被调用,查了源代码才发现这个 DispatchRuntime.Operations.Invoker 没有其他引用,也难怪在赋值前是null(妈的,微软爸爸真是坑爹)。
只能通过实现 IOperationBehavior 来操作这个Invoker。然而 IOperationBehavior 并不能通过配置来实现,查看源代码发现 IOperationBehavior 是 OperationDescription 的 子属性,在 IEndpointBehavior 的 ApplyDispatchBehavior 的 endpoint 参数可以得到 OperationDescription 的引用。那么实现就变成这样了。
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.Text;
namespace WcfExtensions
{
/// <summary>
/// 吠陀终结点扩展
/// </summary>
public class VedaEndpointBehavior : IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
/// <summary>
/// 只有在客户端时才会被调用
/// </summary>
/// <param name="endpoint"></param>
/// <param name="clientRuntime"></param>
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
}
/// <summary>
/// 增加调度器扩展
/// </summary>
/// <param name="endpoint"></param>
/// <param name="endpointDispatcher"></param>
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
endpointDispatcher.DispatchRuntime.ChannelDispatcher.ErrorHandlers.Add(new ExiaErrorHandler());
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new DynamesMessageInspector());
foreach (var operation in endpointDispatcher.DispatchRuntime.Operations)
{
operation.ParameterInspectors.Add(new AstraeaParameterInspector());
}
foreach (var operation in endpoint.Contract.Operations)
{
operation.Behaviors.Add(new VirtueOperationBehavior());
}
}
public void Validate(ServiceEndpoint endpoint)
{
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.Text;
namespace WcfExtensions
{
/// <summary>
/// 德天使方法扩展
/// </summary>
public class VirtueOperationBehavior : IOperationBehavior
{
public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
{
}
/// <summary>
/// 增加方法扩展
/// </summary>
/// <param name="operationDescription"></param>
/// <param name="dispatchOperation"></param>
public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
{
dispatchOperation.Invoker = new SyncNadleehOperationInvoker(operationDescription.SyncMethod);
}
public void Validate(OperationDescription operationDescription)
{
}
}
}
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.ServiceModel.Dispatcher;
using System.Text;
using WcfExtensions.Util;
namespace WcfExtensions
{
/// <summary>
/// 娜德雷同步方法调用者
/// 扩展软爹内部的SyncMethodInvoker
/// 在方法调用前后插入自定义的操作
/// </summary>
public class SyncNadleehOperationInvoker : IOperationInvoker
{
private IOperationInvoker innerInvoker = null;
private MethodInfo method;
private string methodName;
private int inputParameterCount;
private int outputParameterCount;
private Type type;
public SyncNadleehOperationInvoker(IOperationInvoker invoker, MethodInfo method)
{
this.method = method;
innerInvoker = invoker;
inputParameterCount = method.GetParameters().Count(e => e.IsIn);
outputParameterCount = method.GetParameters().Count(e => e.IsOut);
BeforerInvokerHandles.Add(BeforeMethodInvoke.SetStopWatch);
BeforerInvokerHandles.Add(BeforeMethodInvoke.TokenValid);
AfterInvokerHandles.Add(AfterMethodInvoke.OperatingLog);
}
public bool IsSynchronous
{
get
{
return true;
}
}
public MethodInfo Method
{
get
{
return this.method;
}
}
public string MethodName
{
get
{
if (this.methodName == null)
{
this.methodName = this.method.Name;
}
return this.methodName;
}
}
/// <summary>
/// 方法调用前的自定义操作列表
/// </summary>
private List<BeforeMethodInvokeDelegate> BeforerInvokerHandles = new List<BeforeMethodInvokeDelegate>();
/// <summary>
/// 方法调用后的自定义操作列表
/// </summary>
private List<AfterMethodInvokeDelegate> AfterInvokerHandles = new List<AfterMethodInvokeDelegate>();
public object[] AllocateInputs()
{
return innerInvoker.AllocateInputs();
}
/// <summary>
/// 在方法调用前后插入自定义的操作
/// </summary>
/// <param name="instance"></param>
/// <param name="inputs"></param>
/// <param name="outputs"></param>
/// <returns></returns>
public object Invoke(object instance, object[] inputs, out object[] outputs)
{
bool ifBreak = false;
object ret = null;
ConcurrentDictionary<string, object> invokeContext = new ConcurrentDictionary<string, object>();
foreach (var beforeInvoke in BeforerInvokerHandles)
{
var temp = beforeInvoke.Invoke(invokeContext, method, instance, inputs, out ifBreak);
if (ifBreak)
{
ret = temp;
break;
}
}
if (!ifBreak)
{
ret = innerInvoker.Invoke(instance, inputs, out outputs); ;
}
else
{
outputs = new object[outputParameterCount];
}
foreach (var afterInvoke in AfterInvokerHandles)
{
afterInvoke.Invoke(invokeContext, method, instance, inputs, outputs, ret);
}
return ret;
}
public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
{
return innerInvoker.InvokeBegin(instance, inputs, callback, state);
}
public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
{
return innerInvoker.InvokeEnd(instance, out outputs, result);
}
}
}
这样我们就成功侵入到接口方法调度器内部了,我在调度器中增加了 BeforerInvokerHandles 和 AfterInvokerHandles 列表用于在方法调用前后运行其他自定义函数,我在我的demo里增加了token校验和接口日志功能。