使用Simple Injector进行方法级别的属性拦截

时间:2015-03-09 23:35:57

标签: c# dependency-injection ioc-container simple-injector

使用Unity,我能够像这样快速添加基于属性的拦截

public sealed class MyCacheAttribute : HandlerAttribute, ICallHandler
{
   public override ICallHandler CreateHandler(IUnityContainer container)
   {
        return this;
   }

   public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
   {
      // grab from cache if I have it, otherwise call the intended method call..
   }
}

然后我以这种方式注册Unity:

container.RegisterType<IPlanRepository, PlanRepository>(
    new ContainerControlledLifetimeManager(),
    new Interceptor<VirtualMethodInterceptor>(),
    new InterceptionBehavior<PolicyInjectionBehavior>());

在我的存储库代码中,我可以有选择地装饰某些要缓存的方法(可以为每个方法单独定制属性值):

    [MyCache( Minutes = 5, CacheType = CacheType.Memory, Order = 100)]
    public virtual PlanInfo GetPlan(int id)
    {
        // call data store to get this plan;
    }

我正在 Simple Injector 中探索类似的方法。从我读取和搜索的内容看起来只有接口/类型级别拦截可用。但我希望能够选择使用这种类型的属性控制拦截行为来装饰各个方法。有什么建议吗?

[修改:将 Autofac 移至其own question以保持此问题为重点]

1 个答案:

答案 0 :(得分:5)

Simple Injector没有动态拦截的开箱即用支持,因为这不符合其设计原则,如here所述。但是可以添加拦截功能,例如使用Castle DynamicProxy,如here所示。也应该可以在Simple Injector之上使用Unity的拦截功能,但我从未尝试过。

但是,在使用DynamicProxy时,必须将拦截器类与属性类分开。这实际上是一种更好的做法,因为这会保留您的属性passive,并防止强制您的代码库依赖于拦截库。

使用DynamicProxy实现此功能时,它可能如下所示:

public class MyCacheInterceptor : IInterceptor 
{   
    public void Intercept(IInvocation invocation) {
        var attribute = invocation.Method.GetAttribute<MyCacheAttribute>();

        if (attribute == null) {
            // Pass through without applying caching
            invocation.Proceed();
        } else {
           // apply caching here
        }
    }
}
然而,

Simple Injector通过应用SOLID原则和使用装饰器来促进面向方面编程。在我编写的应用程序中,我定义了通用抽象,例如ICommandHandler<TCommand>IQueryHandler<TQuery, TResult>。这使得通过装饰器应用缓存变得微不足道。装饰器的好处在于它们更清洁(因为它们不依赖于任何外部库)并且性能更高。