Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Method-level attributed interception with Simple Injector

With Unity, I'm able to quickly add an attribute based interception like this

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..
   }
}

Then I register with Unity this way:

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

In my repository code, I can selectively decorate certain methods to be cached (with attribute values that can be customized individually for each method) :

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

I'm exploring similar ways to do this in Simple Injector. From what I read and searched looks like only interface/type level interception is available. But I would love the option of decorating individual methods with this type of attribute controlled interception behavior. Any advise?

[Edit: moved Autofac to its own question to keep this question focused]

like image 787
Calvin Avatar asked Mar 09 '15 23:03

Calvin


1 Answers

Simple Injector does not have out-of-the-box support for Dynamic Interception, because this doesn't fit its design principles, as explained here. Interception capabilities can be added however, for instance using Castle DynamicProxy as shown here. It should also be possible to use Unity's interception capabilities on top of Simple Injector, but I never tried this.

When using DynamicProxy however, you will have to separate your interceptor class from the attribute class. This is actually a much better practice, because this keeps your attributes passive and prevents forcing your code base from taking a dependency on the interception library.

When implementing this with DynamicProxy, it might look something like this:

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 however, promotes Aspect-Oriented Programming by applying SOLID principles and using decorators instead. In the applications I write I define generic abstractions such as ICommandHandler<TCommand> and IQueryHandler<TQuery, TResult>. This makes applying caching through decorators trivial. What's nice about decorators is that they are much cleaner (as they don't depend upon any external library) and are much more performant.

like image 65
Steven Avatar answered Sep 29 '22 21:09

Steven