Aspect Oriented Programming

AOP is a neat trend that has been going on for awhile, but only recently have some tools been made (in .NET) to support it. But first, a definition:

In computing, aspect-oriented programming (AOP) is a programming paradigm in which secondary or supporting functions are isolated from the main program’s business logic. It aims to increase modularity by allowing the separation of cross-cutting concerns, forming a basis for aspect-oriented software development. Wikipedia

In plain english, it means you take out common tasks like logging or caching, and reduce them to a similar “aspect” that you can apply to your code more than once. This makes a lot more sense if you see it in action, so here we go.

Let’s look at a example tracing call:

1
2
3
4
5
6
7
8
9
public double CalculateTax(double subtotal, double rate)
{
    using(Tracer trace = new Tracer(MethodBase.GetCurrentMethod()))
    {
        trace.TraceInformation("Starting Calculate method");
        double result = subtotal * rate;
        trace.TraceInformation("Finished Calculate method");
    }
}

As you can see, there’s only one line of actual “business logic” in that method, yet because of tracing requirements we end up with a 4-line method. Using Aspects, we can reduce this to:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class TracingAspect : OnMethodBoundaryAspect
{
    public override void OnEntry( MethodExecutionEventArgs eventArgs)
    { Trace.TraceInformation("Entering {0}.", eventArgs.Method);  }

    public override void OnExit( MethodExecutionEventArgs eventArgs)
    { Trace.TraceInformation("Leaving {0}.", eventArgs.Method);   }
}
 
[Tracing]
public double CalculateTax(double subtotal, double rate)
{
    double result = subtotal * rate;
}

And then reuse the code as easily as:

1
2
3
4
5
[Tracing]
public void OtherMethod(string someArgument)
{
    // some business logic
}

This is all done with a tool called Post Sharp (in .NET anyways). It works by post-processing compiled code to look for those attributes that we add to methods and classes, and inserting the code we create in those Aspect classes into the compiled code – so essentially the compiled code output is the same as our first example, but as far as we’re concerned it’s an entirely separate function we maintain separately. There’s also a few options on how the code is inserted, giving users extra flexibility. For example:

  • OnMethodBoundary – Like our example above, this code will be inserted before and/or after the target method is called
  • OnMethodInvocation – This code is executed before the method is executed, allowing the method call to be intercepted and overriden if necessary. Think of caching – you can check a in-memory cache for a method result instead of executing some SQL code
  • OnException – Basically the same as having a Catch block around your method – except you don’t have to code it.
  • OnFieldAccess – If you need to do something before someone gets or sets a property, use this aspect. Useful for lazy initialization

Now instead of wrapping your code in using statements, or manually checking caches, or copy-and-pasting the same catch block, or throwing exceptions on security errors in your methods, you can write an aspect once and reuse it, like [Tracing] or [Cached] or [LogException] or [RequiresModuleSecurity(123, SecurityLevel.Level1)]

Comments