Adding AOP to Staff Intranet

Because I’m a stickler for good code I put exception handling into my task layer and wrap any exception that may be raised from the data access layer into an appropriate exception for the task layer and also log the exception, because of this I end up having lots of unit test code that looks similar to make sure I’m enforcing this rule:

[Test]
public void Should_log_exception_if_exception_is_raised_when_deleting_session()
{
    Guid id = Guid.Empty;
    IAdministrationRepository mockRepository = CreateMock();

    IServiceResolver mockResolver = CreateMock();
    ILog mockLog = CreateMock();
    Exception mockException = new Exception( "mock ex" );

    using (Record)
    {
        SetupResult.For(mockResolver.Resolve())
                   .Return(mockLog);
        SetupResult.For(mockRepository.FindLoginSessionBy(id))
                   .IgnoreArguments()
                   .Return(new LoginSession(id));
        mockRepository.Delete(null);
        LastCall.IgnoreArguments()
                .Throw(mockException);
        mockLog.Error(mockException);
    }

    using (PlayBack)
    {
        try
        {
            IoC.InitializeWith(mockResolver);
            IAuthenticationTask sut = createSUT(mockRepository);
            sut.InvalidateSessionFor(id);
        }
        catch { }
    }
}

And to fulfill this I then end up with cross cutting code to meet the test behaviour:

try
{
    //... work here
}
catch (Exception thrownException)
{
    Log(thrownException);
    throw new ProblemSavingStaffMemberException(thrownException);
}

To try and cut down on this cross cutting code and to keep the tasks more lean I did some investigation into some AOP strategies.

My first reaction was… I’m using Castle Windsor so can I utilize it’s built in AOP capabilities after changing some code and adding an interceptor I quickly found out this won’t be possible as it doesn’t support out or ref parameters and due to some of my task layer method using out parameters to pass back a notification object this choice was gone!

Next up I had a look at PostSharp the difference between them being that PostSharp adds code in after compilation whereas Windsor dynamically creates proxies at runtime. After looking at some examples I had an idea as to how to implement it so after installing PostSharp I created my object that will inject itself into other objects:

[Serializable]
[AttributeUsage(AttributeTargets.All)]
public class TaskExceptionHandlerAttribute : OnMethodBoundaryAspect
{
    public override void OnException(MethodExecutionEventArgs eventArgs)
    {
        Exception thrownException = eventArgs.Exception;
        LogException(thrownException);
        WrapExceptionWithAttribute wrapExceptionAttribute = RetrieveWrappingExceptionAttribute(eventArgs.Method);
        if (wrapExceptionAttribute != null)
        {
            Type exceptionToWrapWith = wrapExceptionAttribute.WrapExceptionType;
            Exception exceptionToThrow = (Exception)Activator.CreateInstance(exceptionToWrapWith, thrownException);
            if (exceptionToThrow != null)
                throw exceptionToThrow;
        }
        throw new TaskLayerException(thrownException);
    }

    private static WrapExceptionWithAttribute RetrieveWrappingExceptionAttribute(MethodBase method)
    {
        WrapExceptionWithAttribute wrapExceptionAttribute =
        method.GetFirstCustomAttribute(typeof(WrapExceptionWithAttribute),false);
        return wrapExceptionAttribute;
    }

    private static void LogException(Exception thrownException)
    {
        ILog log = IoC.Resolve();
        log.Error(thrownException);
    }
}

In order to not have to place this object as an attribute on all the different task classes I can use the assembly attribute and give it a target using a name plus a wildcard:

[assembly: TaskExceptionHandler(AttributeTargetTypes="MCromwell.StaffIntranet.Task.Tasks.*")]

And that’s it my methods are now injected with the PostSharp code after compilation to handle exceptions and at which point it will call my custom code very cool!

One thing you may have noticed is the use of another attribute that can be decorated on the task methods WrapExceptionWithAttribute this attribute takes a Type in it’s constructor this Type is the exception that should wrap the thrown exception ideally we would like this attribute to be placed on all public task class methods so that we raise applicable exceptions depending on the task be performed although how do we enforce this convention?…

In my next post I will be demonstrating a technique that allows to enforce these types of conventions we want for our systems.

Advertisements