微软家的东西大多是基于消息(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校验和接口日志功能。