Showing posts with label Aop. Show all posts
Showing posts with label Aop. Show all posts

Wednesday, 27 April 2011

AOP - caching with postsharp

Aspect oriented programming is a nice way of keeping your code clean.  Essentially what it allows us to do,  is place various attributes on a method of class declaration which can perform various actions for us without cluttering up our logic.

The example below has a cache aspect declared meaning that the data returned can be cached.

  [Cache(CacheType.Absolute, 120)]  
     public Dictionary<string, string> GetData(string rawUrl)  
     {  

Postsharp extends msbuild and as a result it does extend a compile time a little bit, but it is not obviously noticeable. Using postsharp, we can  hook into the events that happen before and after a method is called. In this case we are intercepting before a method is called.

  [Serializable]  
   public sealed class CacheAttribute : MethodInterceptionAspect  
   {  
     private readonly CacheType m_cacheType;  
     private readonly int m_expiry;  
     public CacheAttribute(CacheType cacheType, int expiry)  
     {  
       m_cacheType = cacheType;  
       m_expiry = expiry;  
     }  
     public override void OnInvoke(MethodInterceptionArgs context)  
     {  
       object value;  
       var key = GenerateKey(context);  
       if (!CacheHelper.Get(key, out value))  
       {  
         // Do lookup based on caller's logic.   
         context.Proceed();  
         value = context.ReturnValue;  
         CacheHelper.Add(value, key, m_cacheType, m_expiry);  
       }  
       context.ReturnValue = value;  
     }  
     private static string GenerateKey(MethodInterceptionArgs context)  
     {  
       var keyBuilder = new StringBuilder();  
       keyBuilder.Append(context.Method.GetHashCode());  
       foreach (var arg in context.Arguments.ToArray())  
       {  
         keyBuilder.Append(arg.GetHashCode());  
       }  
       return keyBuilder.ToString();  
     }  
   }  

Here is the CacheHelper class referenced above:

 public enum CacheType  
   {  
     Absolute,  
     Sliding  
   }  
   public static class CacheHelper  
   {  
     private static CacheItemPolicy GetCachePolicy(CacheType type, int expiry)  
     {  
       var policy = new CacheItemPolicy();  
       switch (type)  
       {  
         case (CacheType.Absolute):  
           policy.AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(expiry);  
           break;  
         case (CacheType.Sliding):  
           policy.SlidingExpiration = new TimeSpan(0, 0, 0, expiry);  
           break;  
       }  
       return policy;  
     }  
     /// <summary>  
     /// Insert value into the cache using  
     /// appropriate name/value pairs  
     /// </summary>  
     /// <typeparam name="T">Type of cached item</typeparam>  
     /// <param name="o">Item to be cached</param>  
     /// <param name="key">Name of item</param>  
     /// <param name="cacheType">Cache Expiration type</param>  
     /// <param name="expiry">Expiry time in seconds</param>  
     public static void Add<T>(T o, string key, CacheType cacheType, int expiry)  
     {  
       var cacheItem = new CacheItem(key, o);  
       MemoryCache.Default.Add(cacheItem, GetCachePolicy(cacheType, expiry));  
     }  
     /// <summary>  
     /// Remove item from cache  
     /// </summary>  
     /// <param name="key">Name of cached item</param>  
     public static void Clear(string key)  
     {  
       MemoryCache.Default.Remove(key);  
     }  
     /// <summary>  
     /// Check for item in cache  
     /// </summary>  
     /// <param name="key">Name of cached item</param>  
     /// <returns></returns>  
     public static bool Exists(string key)  
     {  
       return MemoryCache.Default.Contains(key);  
     }  
     /// <summary>  
     /// Retrieve cached item  
     /// </summary>  
     /// <typeparam name="T">Type of cached item</typeparam>  
     /// <param name="key">Name of cached item</param>  
     /// <param name="value">Cached value. Default(T) if item doesn't exist.</param>  
     /// <returns>Cached item as type</returns>  
     public static bool Get<T>(string key, out T value)  
     {  
       try  
       {  
         if (!Exists(key))  
         {  
           value = default(T);  
           return false;  
         }  
         value = (T)MemoryCache.Default.Get(key);  
       }  
       catch  
       {  
         value = default(T);  
         return false;  
       }  
       return true;  
     }  
   }  

So anytime I want to add caching to a repository now, I just add this aspect (attribute) to my method. The same can be applied for concepts just as easily for logging and transaction handling.

(Neil Duncan wrote this code originally btw!)