AOP with Castle – revisited

AOP with Castle – revisited

A couple of years ago I posted an article demonstrating how Castle DynamicProxy and Castle Windsor can be used to implement concepts of Aspect-Oriented Programming (AOP). The version of Castle that I used at the time was even then an old out of date version. So I thought I’d revisit that little project and  bring it up to date using the latest versions of Castle.Core (contains DynamicProxy) and Castle.Windsor.

The most surprising thing was that almost no changes were actually required. The only actual difference is that IInterceptor is now in the Castle.DynamicProxy namespace instead of Castle.Core.Interceptor namespace, everything else could have been left as is.

So here is the original article repeated, complete with terrible attempt at humour, with only a few edits mostly to update links and style.


 

This article is a short demonstration of using Castle DynamicProxy for a somewhat real world application of Aspect-Oriented Programming (AOP). From version 3, DynamicProxy is contained in the Castle.Core library.

For the sake of this example, imagine, if you will, that you need to count some beans. But not just a few beans, no indeed, you need to count boxes and boxes of beans. For this important task what you need is a dedicated Bean Counter.

public class BoxOfBeans : IEnumerble
{
  // Implementation details ...
}

public class BeanCounter : IBeanCounter
{
  // Details of how we count not important, here just using LINQ.
  // Cast will throw an exception if beans is null.
  public int Count(BoxOfBeans beans) => beans.Cast<object>().Count();
}

For each box of beans you need to count the number of beans in the box and report back the total. If for some reason something goes wrong while counting a box, you still need to count all the remaining boxes. In other words, we’re processing a batch of items in a queue and need to process everything in the queue without one bad item throwing a spanner in the works.

This is where we can take advantage of AOP to manage the error handling without polluting our BeanCounter with a concern other than counting..

Castle DynamicProxy provides an IInterceptor interface that can be used to intercept a method call and modify the behaviour. For example we might define a error handling interceptor for the Count method something like the following:

using Castle.DynamicProxy;

public class ErrorHandlerInterceptor : IInterceptor
{
  public void Intercept(IInvocation invocation)
  {
    try
    {
      // Call Count() ...
      invocation.Proceed();
    }
    catch (Exception exception)
    {
      // Handle the exception ...

      // Set a sensible default bean count ...
      invocation.ReturnValue = 0;
    }
  }
}

The try…catch wraps the call to the Count method, invoked by the invocation.Proceed() line, and handles any exceptions that would otherwise bring the process to a halt. The final line of the catch block sets a default return value since the Count method is expected to always return a value.

The remaining step is to wire up the interceptor to the BeanCounter. There are several ways to do this, but it is very easy if you happen to already be using the Castle Windsor IoC container. You simply need to add an extra component to your IWindsorInstaller for the interceptor:

Component.For<IInterceptor>()
         .ImplementedBy<ErrorHandlerInterceptor>()
         .Named("ErrorInterceptor")

And extend your BeanCounter registration with the interceptor:

Component.For<IBeanCounter>()
         .ImplementedBy<BeanCounter>()
         .Interceptors(
           InterceptorReference
           .ForKey("ErrorInterceptor"))
         .Anywhere

Now, whenever you need an IBeanCounter, you will get a dynamic proxy object implementing the interface that does both the counting and the error handling.

This example is too simple to properly show the benefit of this approach. The real advantage of using an AOP library, like Castle DynamicProxy, is only realised if you have a true cross-cutting concern that is required across several methods and classes. When required multiple times, an interceptor can neatly encapsulate functionality that would otherwise be duplicated across many locations.

A simple working example application based on the above can be found on GitHub: https://github.com/ullet/aop-with-castle-3

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.