Search code examples
structuremapaopinterceptorcastle-dynamicproxymixins

What's the simplest way to intercept a method call for added functionality?


Suppose i have a repository that returns a list of Posts. The repository interface has a GetAll() method which does what it suggests.

Now in keeping with the theory that i shouldn't be putting domain logic in the repository, i want to intercept calls to the concrete GetAll() method such that i can add the following logic to the GetAll() result:

return GetAll().OrderByDescending(p => p.Posted).ToList();

The reason i want to intercept this is because (1) i don't want to have the client remember to call an extension method (OrderByDescending or some useless wrapper of that), i want it called every time and (2) i don't want to have all my concrete implementations have to remember to order the GetAll() result - i want this logic in a single place external to any repository.

What's the easiest way to do this?

I'm already using StructureMap so if i can intercept with this it might be a low cost option. But i don't think SM intercepts method calls, just the creation of the object instance?

Do i need to go to a proxy or mixin pattern? Do i need to go all-in with Castle Dynamic Proxy? Or is there another method i should consider or perhaps a combination?

I'm really interested in a concrete suggestion to my particular example above. I'm novice to AOP so please be gentle.


Solution

  • Went with the DynamicProxy option. It was easier to use than i thought.

    All it took was the using Castle.DynamicProxy; reference...

    A bit of IInterceptor...

    public class PostRepoInterceptor : IInterceptor
    {
        public void Intercept(IInvocation invocation)
        {
            invocation.Proceed();
    
            if (invocation.Method.Name.Equals("GetAll", StringComparison.InvariantCultureIgnoreCase))
                invocation.ReturnValue = this.GetModifiedGetAllResult(invocation.ReturnValue);
        }
    
        private object GetModifiedGetAllResult(object getAllResult)
        {
            return Post.GetOrderedPosts((IList<Post>)getAllResult);
        }
    }
    

    Two new lines in StructureMap config:

        public RepoRegistry()
        {
            var pg = new ProxyGenerator();
    
            For<IPostRepository>()
                .EnrichAllWith(z => pg.CreateInterfaceProxyWithTarget<IPostRepository>(z, new PostRepoInterceptor()));
        }
    

    ..and it's done. GetAll() now behaves how i want. I can still use the interfaces the way i'm familar and i've kept it all DRY and decoupled for DDD.

    Thanks to Sam and Andre.