6

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.

Matt Kocaj
  • 11,278
  • 6
  • 51
  • 79

4 Answers4

13

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.

Matt Kocaj
  • 11,278
  • 6
  • 51
  • 79
2

AFAIK, StructureMap only intercepts object construction, so using it it's not going to work.

I don't know Castle, but I think that the idea - here - is to apply Decorator pattern, so you could also do it by yourself without recurring to a third party library by following the steps described in the previous link.

That's how I'd do it, since I'm not a big fan of AOP.

HTH

Simone
  • 11,655
  • 1
  • 30
  • 43
0

No, it can not change the return value. However, you can access the target inside aspect to change target's property. Assuming you has already Repository defined, here is the code to add post processing aspect to change target property.

IRepository<decimal> Rep = new Repository();
IRepository<decimal> tpRep = (IRepository<decimal>)ObjectProxyFactory.CreateProxy(Rep,
new String[] { "GetAll" },
null,
new Decoration((x, y) =>
{
    Console.WriteLine("Entering " + x.GetType().ToString());
    if (x.GetType().ToString() == "ThirdPartyHR.Repository")
    {
        List<decimal> decimals = ((Repository)x).RepList;
        IEnumerable<decimal> query = decimals.OrderByDescending(num => num, new SpecialComparer()).ToList<decimal>();
        ((Repository)x).RepList = (List<decimal>)query;
    }
}, null));
tpRep.GetAll();
List<decimal> lstRep = Rep.RepList;

If needed, I can send you complete working code. And, if possible, please reply to me from the article "Add Aspects to Object Using Dynamic Decorator" since I don't automatically get the message here.

PatrickSteele
  • 14,489
  • 2
  • 51
  • 54
Gary
  • 1
-1

There is an article Add Aspects to Object Using Dynamic Decorator.

It describes an approach to adding aspects to object at runtime instead of adding aspects to class at design time. Looks like that is what you want.

Matt Kocaj
  • 11,278
  • 6
  • 51
  • 79
Gary
  • 29
  • 1
  • That seems to do pre and post but can it allow the parsing of a return value? Because that's what i really needed in my case (see [my answer](http://stackoverflow.com/questions/4496985/whats-the-simplest-way-to-intercept-a-method-call-for-added-functionality/4516588#4516588)). – Matt Kocaj Jan 17 '11 at 05:57