This article is a short demonstration of using Castle DynamicProxy[1] for a somewhat real world application of Aspect-Oriented Programming (AOP)[2].
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 BeanCounter : IBeanCounter
{
public int Count(BoxOfBeans beans)
{
int count = 0;
// ... do some counting ...
return 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 bothering our BeanCounter with someone elses problem.
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:
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[3] IoC[4] 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[5] based on the above can be found on GitHub: https://github.com/ullet/aop-with-castle
The example application is intentionally using the very old version 2.1 of Castle DynamicProxy to show it is possible even if stuck with legacy code.
Links
- DynamicProxy from the Castle Project: http://www.castleproject.org/projects/dynamicproxy/
- Wikipedia article on Aspect-Oriented Programming (AOP): http://en.wikipedia.org/wiki/Aspect-oriented_programming
- Windsor from the Castle Project: http://www.castleproject.org/projects/windsor/
- Wikipedia article on Inversion of Control (IoC): https://en.wikipedia.org/wiki/Inversion_of_control
- Example application on GitHub: https://github.com/ullet/aop-with-castle