Caching with C#, AOP and PostSharp

I’ve been spending lots of time getting my head around Aspect-Oriented Programming (AOP). At its foundation, AOP separates crosscutting concerns (concerns which are not localizable and cannot be implemented successfully using pure imperative or object-oriented programming) into loosely coupled, modularized units called aspects and injects them into the otherwise one-dimensional base program.  Without AOP, we end up with a system with tangled and scattered code which is harder to design, understand, implement, and evolve primarily due to poor traceability, lower productively, less code reuse and testing difficulties. With AOP, well, my head started to hurt…until I put an ended to my research and put AOP to practice.image

I know it’s a bit of a tease, but I’m going to save my “What is OAP” post for another day.  Right now, I’m going to show you some code.

Postsharp is an open source library which encapsulate aspects as custom attributes and adds new behaviors to your code through policy injection (which is just another name for AOP.) 

Smelly, Old Code

The follow code serves as an example of a crosscutting concern (Cache) which needs to be separated from the base program and modularized into an aspect.  Notice the offending code checks the cache and queries the database.  It laughs in the face of the Single Responsibility Principle.

private const string KeyNumber = "RandomNumber";

private int GetNumber()
{
    int value;
    if (!CacheHelper.Get(KeyNumber, out value))
    {
        value = DataAccess.GetNumberRandom();
        CacheHelper.Add(value, KeyNumber);
    }

    return value;
}

Shiny, Happy Code

Using PostSharp, GetNumber() is left to do one thing (query the database) and all the caching checks are handled by the aspect, the custom CacheAttribute.  Notice this is all hooked up by decorating GetNumber() with the CacheAttribute tag which accepts the key in the constructor.

private const string KeyNumber = "RandomNumber";

// Pass the key associated with the cached item in the constructor
[Cache(KeyNumber)] 
private int GetNumber()
{
    // This logic is only executed if the CacheAttribute calls upon it.
    return DataAccess.GetNumberRandom();
}

The attribute’s logic is basic but very powerful.  When the GetNumber (or any other method which is decorated with the CacheAttribute)  is invoked, CacheAttribute checks to see if the data is in cache using the provided key.  If so, the data is extracted and returned. Period. The GetNumber method is completely bypassed and never executed.  In the sample application, I added some logging to help clarify this point.  However, if the data is not found in cache, the custom attribute invokes the caller, the GetNumber() method, stores the result in cache and then returns the value.  In this case, GetNumber’s logic is executed but it is invoke via the CacheAttribute. 

using System;
using PostSharp.Laos;

namespace PostSharpWebApplicationCache
{
    [Serializable]
    public sealed class CacheAttribute : OnMethodInvocationAspect
    {
        private readonly string key;

        public CacheAttribute(string key)
        {
            this.key = key;
        }

        public override void OnInvocation(MethodInvocationEventArgs context)
        {
            object value;

            if (!CacheHelper.Get(key, out value)) 
            {
                // Do lookup based on caller's logic. 
                value = context.Delegate.DynamicInvoke();  
                CacheHelper.Add(value, key); 
            }

            context.ReturnValue = value;
        }
    }
}

As you can see, both the smelly, old code and the shiny, happy code achieve the same effect, but the latter option is a lot cleaner and more elegant if you ask me.

As noted, I’ve included some downloaded code if you want to play around with Postsharp and AOP on your own.  To get the code to work, you’ll need to visit the Postsharp site and install the latest bits.  In my case, I’m using PostSharp 1.0.  Please don’t be fooled.  You’ll need to use an ASP.NET Web Application Project rather than ASP.NET Web Site in order for PostSharp to work.  This is because Postsharp does compile-time weaving and cleverly mucks with your IL. Details.  Don’t worry. we’ll get into all of it.  For now, just trust me and know the sample project is a Web Application Project.  Enjoy.

 

Download Sample PostSharp Web Project: PostSharpWebApplicationCache.zip

Comments

  1. hey ben:

    nice article! i’ve been using PostSharp with NLog and it works great.

    now i can replace my caching mechanism with PostSharp as you’ve described. thanks for the thoughts!

    -ted

  2. this is slick, but doesn’t work for me because i need to specify the cache key based on values from the delegate parameter list. unfortunately, parameterinfo doesn’t provide a GetValue() method and there’s no simple way to do this with reflection. any ideas?

  3. @Joey – That’s a tough one. Nothing comes to the top of my head. You may want to see if the PostSharp folks can offer any help. They may have ideas. Best of luck.

  4. Great idea – caching with an attribute is very clean and shiny.

    However, this code is broken and won’t compile with the latest version of PostSharp – can anybody post the correct code or a link to some correct code?

  5. Found the solution: there is full sample code for caching in the PostSharp install directory.

  6. Hi there would you mind letting me know which web host you’re utilizing? I’ve loaded your blog in 3 different internet browsers and I must say this blog loads a lot faster then most. Can you recommend a good internet hosting provider at a reasonable price? Many thanks, I appreciate it!

  7. Thanks for all your work on this blog. Debby really likes managing investigation and it’s really simple to grasp why. My spouse and i notice all of the lively tactic you provide insightful ideas via the blog and even cause contribution from visitors on that concern plus our own daughter is actually discovering so much. Take advantage of the remaining portion of the new year. You’re conducting a fabulous job.

  8. Someone essentially help to help make severely posts I might declare. That is that very first precious time As i visited ones own website page and even up to now? As i surprised considering the analysis one which is designed to make this particular put up amazing. Fantastic job!

closed