Smarter ideas worth writing about.

AOP in Castle Windsor

Tags: Cincinnati

Most application development will contain common functionality that spans multiple layers, such as authentication, communication, exception management, and logging. Such functionality is generally thought of as a crosscutting concern because it affects the entire application, and should be centralized in one location. Unfortunately though, how many times have you seen code modifications like this?


The business requirements that are being satisfied here are:

  • If an exception is thrown, log information about the exception at the source
  • Performance metrics on how long a synchronous method takes to complete

This code usually starts in a handful of places, but inevitably it is copied and pasted throughout the application as it is easier than architecting a solution that would be more manageable. 

This approach works of course, however, this creates some rather troubling issues:

  • The exception and performance code are now tightly coupled to the work being done and also to each other.
  • Any changes to the way in which exceptions are logged and timing calculated would need to be replicated in every piece of code.
  • The timing code will always fire and cannot be removed unless the code was removed and redeployed. It would be great if we could remove the timing code via configuration when we deploy to production so we do not impact performance.

How can we align the implementation with better practices? AOP to the rescue.

AOP Background

So what is AOP anyway? To start AOP stands for Aspect Oriented Programming and its goal is to increase modularity by allowing the separation of cross-cutting concerns. It does so by adding additional behavior to existing code without modifying the code itself and in C# is usually done with a class or method attribute. This allows behaviors that are not critical to the business logic to be added to without repeating code or creating tightly coupled code.


To maintain the business requirements we can use our existing IoC Container Castle Windsor. If you are using a different container, you should be able to extract this approach to suit your needs, assuming it supports interception. If you are not familiar with Castle Windsor Interception, you can find more information here.

Example Application

Let’s look at an example console application that simply outputs a list of items. This will give us a good starting point to demonstrate how to use AOP with Castle Windsor. 

The full source code used for this blog can be found at https://github.com/cardinal-solutions/AOPwithCastleWindsor.

Console

class Program
{
    private static WindsorContainer _kernel;
    static void Main(string[] args)
    {
        _kernel = new WindsorContainer();
        _kernel.Install(FromAssembly.This());

        var pData = _kernel.Resolve<IProjectData>();

        try
        {
            Run(pData);
        }
        catch (Exception exception)
        {
            System.Console.WriteLine("Program threw an exception: {0}", exception.Message);
        }
        System.Console.Read();
    }

    static void Run(IProjectData data)
    {
        var d = data.GetAllItems();
        System.Console.WriteLine("Get some Project Data: {0} items", d.Count);
        foreach (var item in d)
        {
            System.Console.WriteLine("\t({0}) Name:{1}, Type:{2}", item.Id, item.Name, item.ItemType);
        }
    }
}

Let’s break down what is going on here.

  • At the context root we are setting up Castle Windsor by using an installer that is registering a concrete implementation to requests for an implementation of IProjectData interface.
  • We then get the instance from Castle Windsor and call Run() that calls the GetAllItems() method on the interface and displays the results in the console.
  • If we run the application the output should be:

How did we get that data? Looking at the installer we can see we are resolving the dependency IProjectData with the implementation of ProjectDataLocal. If you are not familiar with Castle Windsor Installers for registering dependencies, you can find more information here.

public class DataInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
      container.Register(Component.For<IProjectData>()
        .ImplementedBy<ProjectDataLocal>());
    }
}

To keep things simple ProjectDataLocal is a hardcoded list of items that we return, but could just as easily call into a database.

public class ProjectDataLocal : IProjectData
{
    private List<ProjectItem> _masterList;
    public ProjectDataLocal()
    {
        _masterList = new List<ProjectItem>
        {
            new ProjectItem { Id = Guid.Parse("5843b73d-45f7-4284-86cf-c2f07821e01d"), Name ="Item 1", ItemType = "A" },
            new ProjectItem { Id = Guid.Parse("9815b73d-45f7-4284-86cf-c2f07821e01d"), Name ="Item 2", ItemType = "A" },
            new ProjectItem { Id = Guid.Parse("2100b73d-45f7-4284-86cf-c2f07821e01d"), Name ="Item 3", ItemType = "B" },
            new ProjectItem { Id = Guid.Parse("8688b73d-45f7-4284-86cf-c2f07821e01d"), Name ="Item 4", ItemType = "C" }
        };
    }

    public List<ProjectItem> GetAllItems()
    {
        return _masterList;
    }

    public ProjectItem GetItemById(Guid id)
    {
        return _masterList.Single(x => x.Id == id);
    }

    public ProjectItem GetItemByName(string name)
    {
        return _masterList.Single(x => x.Name == name);
    }
}

So far so good, but now we need to update the code to handle the business requirements.

Of course we could fall back into the bad habits and do something like this:

public List<ProjectItem> GetAllItems()
{
  var start = DateTime.Now;
  try
  {
    return _masterList;
  }
  catch (Exception exception)
  {
    LogException(exception);
  }
  finally
  {
    LogTiming(start, DateTime.Now);
  }
}

But we can do better… and since we have Castle Windsor already in place it becomes rather simple. 

Let us first create an Aspect class that will handle two ideas:

  1. We need an attribute to decorate a class or method with to indicate that we want to fire the aspect invocation at run time. This is accomplished by inheriting from the Attribute class.
  2. We need to perform a check to see if the calling class or method has the attribute. This is handled in the CanIntercept() method which puts the code in one place to avoid duplication.

/// <summary>
/// Abstract class to wrap Castle Windsor's IInterceptor to only fire if the method or class is decorated with this attribute.
/// </summary>
public abstract class Aspect : Attribute, IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        if (!CanIntercept(invocation, GetType()))
        {//method is NOT decorated with the proper aspect, continue as normal
            invocation.Proceed();
            return;
        }
        ProcessInvocation(invocation);
    }

    /// <summary>
    /// Determine if the intercepted class or method is decorated with the current attribute
    /// Classes decorated will process if decorated on ALL methods
    /// Methods decorated will process if decorate
    /// </summary>
    /// <param name="invocation"></param>
    /// <param name="type"></param>
    /// <returns></returns>
    private bool CanIntercept(IInvocation invocation, Type type)
    {
        return invocation.TargetType.CustomAttributes.Any(x => x.AttributeType == type) ||
            invocation.MethodInvocationTarget.CustomAttributes.Any(x => x.AttributeType == type);
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="invocation"></param>
    public abstract void ProcessInvocation(IInvocation invocation);
}

This is an abstract class which lets us do two things. First it requires an implementation to be instantiated so we can create and call abstract methods that must be present in the concrete class (ProcessInvocation for example). Second, it will check the class and method for itself since it is also an attribute to determine if it should process the invocation.

For our application we will need two implementations to handle both business requirements. We could of course combine these into one, but then we are stepping backwards and violating single responsibility. 

To create an implementation all we need to do is inherit from Aspect and implement its one abstract method ProcessInvocation().

Each aspect is writing to the console just to keep the example simple. In a real application it would call the logging implementation.


public class ExceptionAspect : Aspect
{
    public override void ProcessInvocation(IInvocation invocation)
    {
        try
        {
            invocation.Proceed();
        }
        catch (Exception exception)
        {
            Console.WriteLine("Exception Intercepted: {0}", exception.Message);
            throw;//re-throw
        }
    }
}

public class TimingAspect : Aspect
{
    public override void ProcessInvocation(IInvocation invocation)
    {
        var sw = Stopwatch.StartNew();
        invocation.Proceed();
        sw.Stop();
        Console.WriteLine("({0})Elapsed: {1}", invocation.MethodInvocationTarget.Name, sw.Elapsed);
    }
}

To wire this up we need to modify our installer file and register the interceptors:

public class DataInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(Component.For<ExceptionAspect>());
        container.Register(Component.For<TimingAspect>());

        container.Register(Component.For<IProjectData>()
            .ImplementedBy<ProjectDataLocal>()
            .Interceptors(
                typeof(ExceptionAspect),
                typeof(TimingAspect)));
    }
}

The first thing required it to register the interceptors with themselves, then we can attach them to our IProjectData by type.

When Castle Windsor resolves the dependency it will inject these into the stack and call the interceptors in succession THEN call the method. 

The basic flow looks something like this:


Notice we placed the TimingAspect last so that we get more accurate performance information on GetAllItems().

If we run our application at this point we will not see any changes since we have not set the attributes to our implementation, so let’s do that now:

[ExceptionAspect]
[TimingAspect]
public List<ProjectItem> GetAllItems()
{
    return _masterList;
}

And our output where we can see the elapsed time printed on the screen:

Now if our application was to throw an exception, we would expect our output to be:

Configuration

Now that we have our AOP solution in place we can start to add configuration based execution. 

Let’s say that a new business requirement is added that requires us to remove TimingAspect from executing in production due to its overhead. For this we head back over to the installer file and make a change.

public class DataInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        var isProd = ConfigurationManager.AppSettings["IsProduction"] == "True";
        container.Register(Component.For<ExceptionAspect>());
        container.Register(Component.For<TimingAspect>());

        var iProjData = Component.For<IProjectData>().ImplementedBy<ProjectDataLocal>();
        iProjData.Interceptors(typeof(ExceptionAspect));

        if (!isProd)
        {
            iProjData.Interceptors(typeof(TimingAspect));
        }

        container.Register(iProjData);
    }
}

As you can see we always intercept ExceptionAspect for the IProjectData dependency, but we only intercept TimingAspect if we have a configuration that has a key set to True. This allows us to move our execution behavior to the configuration where we can selectively turn things on or off without having to recompile the application.

Conclusion

AOP is a clean way to selectively target method wrapping and using an IoC Container makes short work of the implementation. We have setup the ability to easily add new aspects that we can selectively use in our code to meet business needs. So the next time you start to copy and paste code similar to what you see here, stop and think about if AOP is a right fit for your application.  

Share:

About The Author

Principal Consultant
Tom is a Principal Consultant in Cardinal’s Cincinnati Office and is a Microsoft Certified Solutions Developer for Web Applications. Tom has a passion for SOLID coding principles and Agile development. He uses this passion to build ground-up solutions that help his clients maximize their efficiency. Tom also leverages Azure offerings to provide cloud solutions that can scale with his client’s needs.