What is AOP (Aspect Oriented Programming)?
Aspect oriented programming breaks down programming logic in separate concerns. It separates and groups blocks of code that perform a specific operation and that can be applied to or re-used by different pieces of code, be it methods, classes, properties, and so on.
Commonly used examples of functionality that is often implemented using aspects are logging, exception handling, caching, authorization,…
Why use AOP?
You can write aspects as classes to perform specific functionality. These aspects can then be attached to code objects (classes, methods, properties, events,…) as attributes. This means that you only have to write the code once, and attach it anywhere you want with (mostly) one single line of code.
By separating this code from your business logic into aspects, changes made to these aspects have no impact on your business logic. In this way your code becomes much cleaner and robust, and it is much easier to maintain, resulting in fewer defects. And with no need write the same code over and over again, writing less code, that is more robust, means that you can focus on the important parts (the business logic) of your code, and you can be more productive as a programmer and save money on development time.
What can AOP do?
Aspect Oriented Programming can be applied in plenty of usage scenarios
- Logging: Whether you log to logging files, a database, or any other device, it’s up to the logging aspect to determine what and where to log it, so there’s no need to do this inside your application logic over and over again.
- Tracing: When you want to start tracing the performance of your application it can become a tedious task. It becomes even more complicated when you want to be able to turn tracing on and off when debugging or testing your code. By placing your tracing code in an aspect, you can do this in 1 single location, instead of muddling around your business code.
- Exception handling: In production environments you don’t want your exceptions (yes, they will occur) to appear to your user and possibly reveal sensitive information. Aspects can handle these exceptions, take appropriate actions and show user-friendly messages.
- Caching: You can write an aspect that captures a method’s output, store it in a cache, and return it from the cache the next time the method is called again.
- Authorization: Go further than the built-in security functionality and write your own logic to grant or deny access to certain functionality or data.
- Auditing: Keep an audit trail of who accesses or changes what data and when.
- NotifyPropertyChanged: Remember implementing INotifyPropertyChanged into your classes over and over again? This can be solved with 1 aspect applied to your classes as 1 single attribute.
- Even more examples:
- Undo / Redo pattern
- Thread dispatching & synchronization
- Transaction handling
- Persistence
- And so on…
How does PostSharp work?
PostSharp weaves its aspects at compile into your code, so they get executed at the right time.
From the PostSharp website (http://www.sharpcrafters.com/postsharp/documentation#under-the-hood):
Source
Think of the source code for your project as the parts of a car, and the build process as the assembly line in the factory. PostSharp aspects are written as source code, and applied to other source code artifacts in your application using .NET attributes.
Compiler
The compiler for your language takes all of your application’s source files and converts them into executable binaries. It is just one of the many phases of the build process.
PostSharp
PostSharp is a compiler post-processor: it takes the output from the compiler, and instruments your assemblies and executables to execute your aspects at the appropriate times.
Run-time
Once compiled, your application only needs one or two lightweight PostSharp assemblies to execute. No need to ship the factory with the car!
AOP With PostSharp
No better explanation than a real example. In following example I will explain how to get started using PostSharp en create your first aspect for caching. In a second example, I will create another to prove that the caching aspect indeed improves performance with an easy tracing aspect.
Getting started using PostSharp
The first step is to download PostSharp from the PostSharp website at http://www.sharpcrafters.com/postsharp/download. There’s a free Community and a paid Professional Edition available. A comparison of the features of each version can be found on this page: http://www.sharpcrafters.com/purchase/compare.
The sample application: Ordering pizza’s
We start from the real beginning by creating a sample application. I’m creating a “Pizza Ordering System” in a MVC3 Web Application. To make development easier, I will use Entity Framework with SQL Server Compact Edition and MVC Scaffolding. This allows me to write a few model classes and let the scaffolding generate controllers and views for me. The Code First feature of Entity Framework creates the database for me based on the model.
This creates a good starting point to begin this example.
First of all we’ll add a reference to the PostSharp.dll (SharpCrafters announced that they will have NuGet package available very soon, in the meantime we’ll have the add it the old-fashioned way.
And because I want to quickly set up a sample application, I install the EntityFramework.SqlServerCompact and MvcScaffolding packages from NuGet. These packages install their dependencies themselves, so I don’t need to take care of that.
I create 3 Model classes, Pizza, PizzaSize and Order for our application, and use the Scaffold command to create controllers and views for them.
Now, as you can see when you take a look at the controllers, the scaffolding created a DbContext that is used and directly addressed in each of the controllers. This isn’t quite useful when we want to use caching. We need some sort of service or repository pattern for this. Let’s instruct scaffolding to use a repository J (I could have done that like this from the beginning, but I just also wanted to show some of the functionality and strength from the MvcScaffolding package):
Remember, when you instruct scaffolding to recreate controllers and datacontext, it needs to recreate the database when you changed something in your model classes. Follow the instructions in the context file to achieve this.
I also created 3 menu items to the Index action of each of these controllers, to make navigation easier.
Now, let’s create a really simple and straight-forward caching class. I know you can do this with the Caching Application block from the Enterprise Library, or some other framework, but I just want to keep the sample straight forward, and since the caching isn’t the subject from this blog post, I don’t go deeper into the caching subject.
public class Cache
{
private static readonly IDictionary<string, object> _cache = new Dictionary<string, object>();
private const int _timeout = 60 * 60 * 24;
public static bool Contains(string key)
{
return _cache.ContainsKey(key);
}
public static object Get(string key)
{
if (_cache.ContainsKey(key))
{
return _cache[key];
}
return null;
}
public static void Add(string key, object item)
{
Add(key, item, _timeout);
}
public static void Add(string key, object item, int timeout)
{
if (_cache.ContainsKey(key))
{
_cache.Remove(key);
}
_cache.Add(key, item);
}
public static void Remove(string key)
{
_cache.Remove(key);
}
public static string GenerateKey(Arguments arguments)
{
var key = new StringBuilder();
foreach (var argument in arguments)
{
key.AppendFormat("_{0}_{1}", argument.GetType(), argument);
}
return key.ToString();
}
}
This creates an in-memory cache and supports adding, retrieving, removing and checking the presence of an object in the cache. It also has a GenerateKey that I will use later to generate a unique key based on the arguments passed to the method that I want to cache the result from.
The caching aspect
Now, time for some action, create the caching aspect!
Start by creating an “Aspects” folder (we want our project to stay clean of course) and create a new class called “CacheAttribute”. To have our aspect execute code before and after a method is called, it must be derived from the OnMethodBoundary aspect parent class. Also, this class needs to be serializable, so apply the [Serializable] attribute.
To execute code before and after a method call, we must implement the OnEntry and OnSuccess methods. In the OnEntry we will check whether the item already exists in the cache, skip the further execution of the method, and set the return value as our cache value. In the OnSuccess method, we will add the return value to the cache.
[Serializable]
public class CacheAttribute : OnMethodBoundaryAspect
{
public override void OnEntry(MethodExecutionArgs args)
{
var key = args.Method + "_" + Cache.GenerateKey(args.Arguments);
var value = Cache.Get(key);
if (value == null)
{
args.MethodExecutionTag = key;
}
else
{
args.ReturnValue = value;
args.FlowBehavior = FlowBehavior.Return;
}
}
public override void OnSuccess(MethodExecutionArgs args)
{
var key = args.MethodExecutionTag.ToString();
Cache.Add(key, args.ReturnValue);
}
}
The next step is to apply the attribute to the methods that we want to cache the result from. We do this by applying the Cache attribute to the All(), AllIncluding() and Find(int id) methods of the PizzaRepository class that scaffolding created for each of our model classes.
When we launch the debugger after we have set breakpoints in the OnEntry() and OnSucces() methods of the CacheAttribute class, and in the All(), AllIncluding() and Find(int id) methods of the PizzaRepository class, we can see that the OnEntry() method of the CacheAttribute is executed first. When the PizzaRepository methods are executed the first time the execution is passed to the original method, and the result is stored in the cache after it is completed. The next time, the method execution is skipped, and the results are directly returned from the cache.
Nice, isn’t it? But does this really improve the performance of our application?
The performance aspect
To answer this question, we’ll create another aspect to trace the time of the execution of a method, the TimeTracingAttribute.
Again, start by creating an aatribute, called “TimeTracingAttribute” in the “Aspects” folder, make it Serializable and inherit from OnMethodBoundaryAspect.
Again, we use the OnEntry() and OnExit () methods, together with a Stopwatch this time. The Stopwatch will be a static instance on the TimeTracingAttribute. In the OnEntry() method we will store the value of the ElapsedTicks property in the MethodExecutionTag property of the attribute’s args. In the OnExit() method we’ll read it out to calculate the executed time (in ticks) and write that to the Trace.
[Serializable]
public class TimeTracingAttribute : OnMethodBoundaryAspect
{
static Stopwatch _stopwatch = new Stopwatch();
static TimeTracingAttribute()
{
_stopwatch.Start();
}
public override void OnEntry(MethodExecutionArgs args)
{
args.MethodExecutionTag = _stopwatch.ElapsedTicks;
}
public override void OnExit(MethodExecutionArgs args)
{
var executionTime = _stopwatch.ElapsedTicks - (long) args.MethodExecutionTag;
Trace.WriteLine(string.Format("{0}: {1} ticks.", args.Method.Name, executionTime));
}
}
Now, apply this TimeTracing attribute to the Index() and Details(int id) methods of the PizzaController and the Create() and Edit(int id) methods of the OrdersController.
When you start the debugger of Visual Studio, you will see the output of the TimeTracingAttribute written to the output window when you open the Pizza page or Edit an order multiple times. See the performance boost?
Now, this is nice when we have Visual Studio open in debugging mode, it would be even nice when we can see the results outside of Visual Studio. We don’t to that in our aspect, it has even nothing to do with AOP, but with another gem that is available from NuGet: Glimpse.
Glimpse is a web debugger used to gain a better understanding of what’s happening inside of your webserver. From the Glimpse website:
What Firebug is for the client, Glimpse does for the server… in other words, a client side Glimpse into what’s going on in your server.
Get the Glimpse package from NuGet, rebuild your application and start it in the browser. One action we must take before we can see Glimpse at work, is enabling it for our application. Do this by launching the /Glimpse/Config page of your browser and click the big “Turn Glimpse On” button.
Now when you open your page again, you will see a small eye-con in the bottom-right corner of you browse which will open the Firebug for your server. Clicking on it will open the Glimpse window with tracing information in the “Trace” tab.
Conclusion
Aspect Oriented Programming (AOP) with PostSharp, or another AOP tool significantly improves robustness of your application and keeps your code clean. It also improves productivity of the development team and allows developers to focus on their important tasks.