WCF 扩展之我见: IOperationInvoker

本系列索引,请见《开篇

说完了 ParameterInspector,本文将开始介绍与方法调用密切相关的 OperationInvoker。它基于 InstanceProvider 中所提供的 Instance,及 Message 中所携带的参数发起方法调用。

作 用

正如其名,该扩展用于调用 operation 所对应的 CLR 方法。我们可以利用该扩展对方法来实现自己的调用逻辑,比如对调用进行缓存。

执行时机

当发起一个 web service 的请求时,其会被转换为对一个 CLR 方法的调用,而 OperationInvoker 就是起到了对具体 CLR 方法的调用。

WCF 内置的实现方式

针对同步调用和异步调用,WCF 提供了两个实现(都是 internal,无法公用):SyncMethodInvoker 和 AsyncMethodInvoker。这两个 Invoker 最终都是利用 反射发出(i.e. Emit) 方式进行方法调用,所以比直接通过反射调用性能会好很多。

SyncMethodInvoker 最终会调用 InvokeMethod:

如果想了解反射和委托的更多信息,请参考我之前写的文章《C# 基础回顾: Delegate 前世今生》。

如何扩展

先来看一下接口的定义(由于这个接口中的方法不同于其它类型的扩展,所以加了注释以便于理解):

    public interface IOperationInvoker
    {   
        //指示了是使用其中的 Invoke 方法还是 InvokeBegin 方法(异步)
        bool IsSynchronous { get; } 
        
        //返回与方法调用所需参数个数相同的空数组,会作为 parameters 传给 MessageFormatter 中的 DeserializeRequest 方法
        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);
    }


我们可以自己写一个类从头到尾实现该接口,也可以利用内置的 OperationInvoker 来省去大部分代码,只是加上我们自己的逻辑。实现了自己的扩展后,需要使用 Behavior 关联到 WCF 运行时,请参考《WCF 扩展之我见: Behaviors》。


举个粟子

下面来实现一个简单版本的缓存调用

public class MyCacheInvoker : IOperationInvoker
{
    IOperationInvoker _invoker;
    string _name;
    public MyCacheInvoker(IOperationInvoker originalInvoker, string name)
    {
        _invoker = originalInvoker;
        _name = name;
    }

    public object[] AllocateInputs()
    {
        return _invoker.AllocateInputs();
    }

    public object Invoke(object instance, object[] inputs, out object[] outputs)
    {
        try
        {
            //根据参数及方法名称生成一个唯一的 key
            string key = GenerateKey(_name, inputs);
            CacheItem result = MemoryCache.Default.GetCacheItem(key);
            if (result == null)
            {
                object obj = _invoker.Invoke(instance, inputs, out outputs);
                
                //这里利用了 .Net 4.0 内置的 MemoryCache
                MemoryCache.Default.Add(key, new object[] { obj, outputs }, new CacheItemPolicy { SlidingExpiration = new TimeSpan(0, 20, 0) });

                return obj;
            }
            else
            {
                object[] cachedItems = result.Value as object[];
                outputs = cachedItems[1] as object[];
                return cachedItems[0];
            }
        }
        catch (Exception e)
        {
            //TODO: log
            throw;
        }
    }
}

public class MyBehavior : Attribute, IOperationBehavior
{
    public void AddBindingParameters(OperationDescription operationDescription, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
    {
    }

    public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
    {
    }

    public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
    {
        dispatchOperation.Invoker = new MyCacheInvoker(dispatchOperation.Invoker, operationDescription.SyncMethod.Name);
    }

    public void Validate(OperationDescription operationDescription)
    {
    }
}

上面这个方法只是为了演示,有很多不足之处(比如没有错误处理、缓存的配置是硬编码等),请勿用于正式产品开发环境。


参考资源

WCF Extensibility – IOperationInvoker

Developing Custom OperationInvoker

文章索引

[隐 藏]

本站采用知识共享署名 3.0 中国大陆许可协议进行许可。 ©2014 Charley Box | 关于本站 | 浙ICP备13014059号