This project is read-only.

Interceptor implemented with attribute

Nov 12, 2013 at 7:39 PM
Edited Nov 12, 2013 at 7:40 PM
Hi, Unity has a Microsoft.Practices.Unity.InterceptionExtension.HandlerAttribute, that allows me to build an attribute interception policy, like I implemented some time ago:
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
    public sealed class CacheAttribute : HandlerAttribute
    {
        public TimeSpan Lifespan
        {
            get { return new TimeSpan(Days, Hours, Minutes, Seconds, Milliseconds); }
        }

        public int Days { get; set; }
        public int Hours { get; set; }
        public int Minutes { get; set; }
        public int Seconds { get; set; }
        public int Milliseconds { get; set; }

        public override ICallHandler CreateHandler(IUnityContainer container)
        {
            return new CacheCallHandler(container.Resolve<ICacheKeyBuilder>())
            {
                Order = Order
            };
        }
    }
CacheCallHandler is a custom class I've created that implements ICallHandler, which gives me the opportunity to intercept the call.

How simple injector handles this? I saw that it provides an IInterceptor interface, but how can I use it with an attribute?
Nov 12, 2013 at 7:53 PM
I'm unfamiliar with Unity's call handler attributes, so it's hard to give any feedback on this. Simple Injector contains no attributes at all that you can use in your application, since using these attributes would make your complete application dependent on the DI container, which is not a good practice.

I might still be able to describe how to do this with Simple Injector, if you can give a bit more information about use case? What services would you decorate with this attribute and what should be the effect of this?
Nov 12, 2013 at 7:58 PM
You're right about the app depending on the DI.
Well, it is a cache system, I will use in the a few repositories, caching the result in memory (using MemoryCache) for a determined time span.
With attribute, I could set different time span for different methods. For example, some method I'd like to cache for 2 minutes, others for 30 seconds...
Nov 12, 2013 at 8:36 PM
Edited Nov 12, 2013 at 8:38 PM
I think you have a few options here.

Your first option is to use decorators instead. I personally prefer the use of decorators, and it's not a coincident that Simple Injector is optimized to work with decorators. If you hide your repositories behind a generic IRepository<TEntity> abstraction, it is rather easy to create a CachingRepositoryDecorator<TEntity> that allows caching. A good example of this can be found in this Stackoverflow question.

If decorators are -for whatever reason- out of the question, the InterceptWith wiki example might still be of help. I made a tiny tweak to the extension method and added a new InterceptWith overload that accepts a Func<ExpressionBuiltEventArgs, IInterceptor>. That means that upon interception, the supplied delegate will be called and this allows you to create an interceptor specially for the given type. Here's an example:
container.InterceptWith(
    e => new CachingInterceptor(e.RegisteredServiceType), 
    type => type.Name.EndsWith("Repository"));
I hope this helps.
Nov 13, 2013 at 11:22 AM
Edited Nov 13, 2013 at 12:39 PM
With Intercept I can't specify the amount of time to cache, can I somehow?
And the repositories did not share an interface. They are quite different.
Nov 13, 2013 at 12:01 PM
What you would have done with Unity is probably something like this:
[Cache(Minutes = 5)]
public interface ICustomerRepository : IRepository { }
You can do the same with Simple Injector, although this CacheAttribute should be a vanilla attribute, not something that inherits from HandlerAttribute. When using the InterceptWith you will have to do something like this:
container.InterceptWith(
    e => new CachingInterceptor(e.RegisteredServiceType.GetCustomAttribute<CacheAttribute>().Lifespan), 
    type => type.GetCustomAttribute<CacheAttribute>() != null);
Nov 13, 2013 at 12:39 PM
Why IInterceptor is not part of the library?
Here is the Unity implementation, for curiosity:
public class CacheCallHandler : ICallHandler
    {
        private readonly ICacheKeyBuilder keyBuilder;

        public CacheCallHandler(ICacheKeyBuilder keyBuilder)
        {
            this.keyBuilder = keyBuilder;
        }

        public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
        {
            var attribute = input.MethodBase.GetCustomAttributes(typeof(CacheAttribute), false).FirstOrDefault() as CacheAttribute;
            if (attribute == null)
                return getNext()(input, getNext);

            var key = keyBuilder.Build(input);
            var entry = MemoryCache.Default.Get(key);
            if (entry != null)
                return input.CreateMethodReturn(entry);

            var result = getNext()(input, getNext);
            if (result.Exception == null)
                MemoryCache.Default.Add(key, result.ReturnValue, DateTime.Now.AddMilliseconds(attribute.Lifespan.TotalMilliseconds));
            return result;
        }

        public int Order { get; set; }
    }
Nov 13, 2013 at 1:44 PM
> Why IInterceptor is not part of the library?

Support for interception is not part of the framework because:
  • Simple Injector is designed to be fast by default. Applying decorators in Simple Injector is just as fast as normal injection, while applying interceptors has a much higher cost, since there is always reflection involved when using an intercepted type.
  • The decorator functionality of Simple Injector is very mature and flexible and interception functionality should (IMO) only be added when the functionality is comparable to that of the decorator sub system (since that is what users will expect), but this is an awful lot of work to get interception to that level. The current code snippet in the documentation doesn't even come close to the completeness of the decorator support (although it works fine for the common case).
  • Simple Injector tries to push developers into good design, and the use of interception is often an indication of a suboptimal design. We like to stimulate developers into using decorators instead. If you can't apply a decorator around a group of related types, you might be missing a common (generic) abstraction. Take a look at this article and this article for some ideas about this.
  • To do interception, you always need a dependency on your interception library, since this library defines an IInterceptor interface or something similar (such as Unity's ICallHandler). Decorators on the other hand can be created without being forced to take a dependency on a framework. Since vendor lock-ins should be prevented, the Simple Injector library doesn't define any interfaces or attributes that should be used on application level. In other words, Simple Injector can't do interception, because that would violate our own design rules.
Does that make sense?
Marked as answer by dot_NET_Junkie on 2/26/2014 at 1:15 PM
Nov 13, 2013 at 6:32 PM
Totally. SimpleInjector rocks!
I love best practices. We decided to use decorators, it was awesome solution.
Nov 16, 2013 at 5:52 PM
Just a side note about the generic repository: http://codebetter.com/gregyoung/2009/01/16/ddd-the-generic-repository/
It has a lot of discussion on the web about the anti-pattern that has been adopted in the repository pattern. It worths checking it out.
Nov 27, 2013 at 9:05 PM
Edited Nov 28, 2013 at 7:01 AM
Note that the Greg deliberately chooses to break the Interface Segregation Principle to use Domain Driven Design. In fact, the repositories Greg is talking about are different from the repositories I talk about in my blog. The repositories Greg uses are purely used by the domain layer. On the other hand, the repositories I describe are often used directly by the presentation layer. That's a big distinction, because it is much less likely that DDD repositories need to be decorated with cross-cutting concerns than publicly accessible repositories would. For instance, when running inside the domain code, there's no need to check the user's right to access some repository; this is done at a higher level.

Still, the discussion is very interesting and it is always important to know and understand the different views. The generic IRepository<T> is interesting in conjunction with an IQueryHandler<TQuery, TResult> that are accessed from outside the domain and makes it very easy to add cross-cutting concerns, but is less suitable for use inside the (complex) domain.

I must admit that in the application I'm working on we don't even have an IRepository<T> interface in our business layer.