Rory Primrose

Learn from my mistakes, you don't have time to make them yourself

View project on GitHub

AOP with PostSharp.Laos

PostSharp.Laos is a Lightweight Aspect-Orientated System for PostSharp that makes it really easy to include AOP in your code. In this demo, PostSharp will be used to aspect the RunTest() method in the following application. The aspect will output console messages before and after the aspected method is executed.

using System;
     
namespace ConsoleApplication1
{ 
    internal class Program
    {
        private static void Main(string[] args)
        {
            RunTest();
     
            Console.ReadKey();
        }
     
        private static void RunTest()
        {
            Console.WriteLine("Running the test method");
        }
    }
}    

Project references required

The project requires PostSharp.Public.dll and PostSharp.Laos.dll references in order to create an aspect attribute.image

As you can see, I also added in a strong name key to see if it caused any issues with PostSharp. It didn’t.

Code required

I know that PostSharp.Laos is easy to use in comparison to PostSharp.Core, but I was honestly shocked at just how easy it really is. To put this demo together, it look somewhere in the order of two minutes.

In order to aspect the RunTest method in the code defined above, an OnMethodInvocationAspect derived attribute was created for the aspect and declared against the RunTest method.

using System;
using PostSharp.Laos;
     
namespace ConsoleApplication1
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            RunTest();
     
            Console.ReadKey();
        }
     
        [MethodTraceAspect]
        private static void RunTest()
        {
            Console.WriteLine("Running the test method");
        }
    }
     
    [Serializable]
    internal class MethodTraceAspectAttribute : OnMethodInvocationAspect
    {
        public override void OnInvocation(MethodInvocationEventArgs context)
        {
            try
            {
                Console.WriteLine("Calling {0}", context.Delegate.Method);
     
                context.Proceed();
            }
            finally
            {
                Console.WriteLine("Called {0}", context.Delegate.Method);
            }
        }
    }
}    

PostSharp will find the MethodTraceAspect attribute on the RunTest method and change the IL so that the OnInvocation method of the attribute is invoked instead of the RunTest method. The context.Proceed() method in the attribute then invokes the RunTest method.

IL result

PostSharp kicks in after the original code has been compiled and changes the IL to include the aspect. Reflector shows the following outcome as the combination of the compiler and PostSharp doing their work.

using System.Reflection;
using System.Runtime.CompilerServices;
using PostSharp.Laos;
     
namespace ConsoleApplication1
{
    internal class Program
    {
        [CompilerGenerated]
        static Program()
        {
            if (!~PostSharp~Laos~Implementation.initialized)
            {
                LaosNotInitializedException.Throw();
            }
            ~PostSharp~Laos~Implementation.~targetMethod~1 = methodof(Program.RunTest);
            ~PostSharp~Laos~Implementation.MethodTraceAspectAttribute~1.RuntimeInitialize(~PostSharp~Laos~Implementation.~targetMethod~1);
        }
     
        private static void ~RunTest()
        {
            Console.WriteLine("Running the test method");
        }
     
        private static void Main(string[] args)
        {
            RunTest();
            Console.ReadKey();
        }
     
        [DebuggerNonUserCode, CompilerGenerated]
        private static void RunTest()
        {
            Delegate delegateInstance = new ~PostSharp~Laos~Implementation.~delegate~0(Program.~RunTest);
            MethodInvocationEventArgs eventArgs = new MethodInvocationEventArgs(delegateInstance, null);
            ~PostSharp~Laos~Implementation.MethodTraceAspectAttribute~1.OnInvocation(eventArgs);
        }
    }
     
    [Serializable]
    internal class MethodTraceAspectAttribute : OnMethodInvocationAspect
    {
        public override void OnInvocation(MethodInvocationEventArgs context)
        {
            try
            {
                Console.WriteLine("Calling {0}", context.Delegate.Method);
                context.Proceed();
            }
            finally
            {
                Console.WriteLine("Called {0}", context.Delegate.Method);
            }
        }
    }
}
     
[CompilerGenerated]
internal sealed class ~PostSharp~Laos~Implementation
{
    internal static MethodBase ~targetMethod~1;
    internal static bool initialized;
    internal static readonly MethodTraceAspectAttribute MethodTraceAspectAttribute~1;
     
    static ~PostSharp~Laos~Implementation()
    {
        try
        {
            object[] objArray = (object[]) Internals.DeserializeFromResource(typeof(~PostSharp~Laos~Implementation).Assembly, "~PostSharp~Laos~CustomAttributes~");
            MethodTraceAspectAttribute~1 = (MethodTraceAspectAttribute) objArray[0];
            initialized = true;
        }
        catch (Exception exception)
        {
            Debugger.Log(30, "PostSharp", exception.Message);
            throw;
        }
    }
     
    [Serializable]
    public delegate void ~delegate~0();
}

The important part to note is that the contents of RunTest() have been moved into ~RunTest() and have been replaced with a call to MethodTraceAspectAttribute.OnInvocation. A delegate to ~RunTest() is passed in the arguments to OnInvocation so that the aspect can then invoke the original method.

Compiled assembly references

After PostSharp has finished weaving the aspect into the IL, the assembly still has a reference to PostSharp.Laos.dll and PostSharp.Public.dll.

Debugging support

Debugging the assembly after it has been aspected is seamless. When the Main method invokes RunTest(), the debugger steps into the MethodTraceAspectAttribute.OnInvocation method. Stepping into context.Proceed() takes you into the RunTest() method in the source code (which is actually ~RunTest in the compiled assembly). Stepping out of RunTest takes you back into the MethodTraceAspectAttribute.OnInvocation method.

Runtime result

Result

License implications

My understanding of the PostSharp license (read here and here) is that this implementation does not require a commercial license as PostSharp.Core.dll does not need to be bundled with the application for distribution.

Advantages

PostSharp.Laos is very easy to use. So easy in fact that I am surprised this product isn’t much more famous than it already is. Something this powerful that is so easy to use is a major advantage especially because it can be implemented for free.

Disadvantages

There are several disadvantages using PostSharp.Laos. The importance of each of these disadvantages will depend on your unique scenario.

  • The final assembly requires references to PostSharp.Laos.dll and PostSharp.Public.dll.
  • The requirement for the two PostSharp assemblies to be shipped may be an issue because of increased packaging and dependencies. I prefer projects to be as clean and lean as possible so the less references the better.
  • The reference to the aspected method identifies it as ~RunTest rather than RunTest
  • The changed name of the aspected method is only going to be an issue when the aspect implementation does something with that name. Tracing the method name (as in the case of this demo) may create confusion. The aspect could strip the leading ~, however no assumption about such a behaviour from PostSharp should be made.
  • The IL isn’t very clean with all the code used to redirect the RunTest method invocation through the aspect.
  • If you spend a lot of time in Reflector, the IL generated for the RunTest implementation will be a little confusing especially if compared against the original source code.

Conclusion

Wow, this is an unbelievable product. If you want to get some easy advantages out of AOP and don’t mind the few disadvantages stated above, then PostSharp.Laos is your new best friend.

Written on April 11, 2009