AOP – Implementing try catch in C# with PostSharp

I’ll be honest, I always cringe whenever I have to use try/catch. I do everything I can to avoid it. Besides being a resource hog, try/catch statements clutter code making it harder to read. So naturally I was excited when learning about Aspect-oriented programming (AOP) and PostSharp (the AOP library for C#) that try/catch statements are cross-cutting concerns and can be implemented as aspects.

It is assumed you have the following already installed and licensed:

  1. Visual Studio
  2. PostSharp

Step 1 – Create your Visual Studio Project

So to show you how this works, lets get started.

  1. Create a sample Console Application project in Visual Studio.
  2. Add a reference to PostSharp.
  3. Populate the Program.cs with an example exception as shown.
using System;
using Common.Aspects;

namespace AspectExamples
{
    class Program
    {
        static void Main(string[] args)
        {
            ThrowSampleExecption();
        }

        private static void ThrowSampleExecption()
        {
            throw new ApplicationException("Sample Exception");
        }
    }
}

Ok, now we have an example exception, lets show how we can catch this exception, log it, and continue.

Step 2 – Creating an exception aspect

  1. Right-click on your project and choose Add | Class.
  2. Give the class a name.
    I named my class ExceptionAspect.
  3. Click OK.
  4. Make the class inherit from OnExceptionAspect.
    Note: If you haven’t added a reference to PostSharp, you’ll need to do that now.
  5. Add a string property called Message.
  6. Add a Type property called ExceptionType.
  7. Add a FlowBehavior property called Behavior.
    The default value is FlowBehavior.Default.
  8. Override the OnException method.
  9. In the OnException method, create a message and log it.
    For this example, I will just log to the Visual Studio  Output window.
  10. In the OnException method, set the FlowBehavior to the Behavior property.
  11. Override the GetExceptionType method and configure it to return the ExceptionType property.
using System;
using System.Diagnostics;
using PostSharp.Aspects;

namespace Common.Aspects
{
    [Serializable]
    public class ExceptionAspect : OnExceptionAspect
    {
        public String Message { get; set; }

        public Type ExceptionType { get; set; }

        public FlowBehavior Behavior { get; set; }

        public override void OnException(MethodExecutionArgs args)
        {
            string msg = DateTime.Now + ": " + Message + Environment.NewLine;
            msg += string.Format("{0}: Error running {1}. {2}{3}{4}", DateTime.Now, args.Method.Name, args.Exception.Message, Environment.NewLine, args.Exception.StackTrace);
            Debug.WriteLine(msg);
            args.FlowBehavior = FlowBehavior.Continue;
        }

        public override Type GetExceptionType(System.Reflection.MethodBase targetMethod)
        {
            return ExceptionType;
        }
    }
}

Your ExceptionAspect class is complete and ready to use.

Step 3 – Apply the ExceptionAspect

  1. Add ExceptionAspect as an attribute to the ThrowSampleException method.
  2. Set the ExceptionType property to type of exception being thrown, which is an ApplicationException in this example.
  3. Set the Message property to hold a message.
  4. Set the Behavior property to FlowBehavior.Continue.
using System;
using Common.Aspects;
using PostSharp.Aspects;

namespace AspectExamples
{
    class Program
    {
        static void Main(string[] args)
        {
            ThrowSampleExecption();
        }

        [ExceptionAspect(ExceptionType = typeof(ApplicationException), Message = "An example exception.", Behavior = FlowBehavior.Continue)]
        private static void ThrowSampleExecption()
        {
            throw new ApplicationException("Sample Exception");
        }
    }
}

This is now complete. You have now implemented try/catch as an aspect.
You should take the time to look at your code with .NET Reflector or ILSpy to see what is really being done.
So here is the resulting code for the method accord to ILSpy.

  • Notice that our one line of code in the original method is in the try block.
  • Notice that in the catch block, the OnException method is called.
  • Notice that the catch block also has a switch statement based on the FlowBehavior to determine whether to continue or rethrow, etc.
// AspectExamples.Program
private static void ThrowSampleExecption()
{
	try
	{
		throw new ApplicationException("Sample Exception");
	}
	catch (ApplicationException exception)
	{
		MethodExecutionArgs methodExecutionArgs = new MethodExecutionArgs(null, null);
		MethodExecutionArgs arg_1F_0 = methodExecutionArgs;
		MethodBase m = Program.<>z__Aspects.m2;
		arg_1F_0.Method = m;
		methodExecutionArgs.Exception = exception;
		Program.<>z__Aspects.a0.OnException(methodExecutionArgs);
		switch (methodExecutionArgs.FlowBehavior)
		{
		case FlowBehavior.Default:
		case FlowBehavior.RethrowException:
			IL_55:
			throw;
		case FlowBehavior.Continue:
			methodExecutionArgs.Exception = null;
			return;
		case FlowBehavior.Return:
			methodExecutionArgs.Exception = null;
			return;
		case FlowBehavior.ThrowException:
			throw methodExecutionArgs.Exception;
		}
		goto IL_55;
	}
}

You now can implement pretty much any try/catch block as an Aspect using the ExceptionAspect.

Reusability

One of the main benefits of Aspects is that they take cross-cutting concerns and make them modular. That means that like class files, they are now reusable. Now it is easy to use, link to, or copy and paste your ExceptionAspect class into any other project.

Return to Aspected Oriented Programming – Examples

3 Comments

  1. bN says:

    Hi,

    I think it could be dangerous to use FlowBehavior.Continue like in your sample, because it could hide an important issue that would let the system in an inconsistent state. This kind of issue can be very hard to debug (when in production), even if you have exceptions loggeg. I think it is better to throw the exception and let the uppermost layer handle it. In that case, the purpose of the aspect would be only to log exceptions, not to handle it (it could be renamed ExceptionLogAspect). In my opinion it is a better design because an exception handling can be very different according to various situations. There are situations in which simply log and continue is the best thing to do but it is not always the case. So, we have to compose with "try..catch", at least in the uppermost layer.

    • Rhyous says:

      That is true.
      Remember this is not production code, but blog example code.
      In production environments, you should only ignore exceptions if
      1. You intend to ignore them for a clear reason
      2. You log them and then have IT monitoring the log of errors

  2. Diego says:

    It's wonderful the inheritance that one can obtain with PostSharp creating derived aspects, customizing and extending the base aspects.

Leave a Reply

How to post code in comments?