Tim McCarthy's Blog

Stuff I think about...

My Links

Blog Stats

News

Archives

Post Categories

Image Galleries

A better way to encapsulate caching in .NET 2.0

I used to have a class that I used quite a bit in my web projects that allowed me to encapsulate the algorithm for getting/setting items from the ASP.NET Cache.

Here is what the class looked like in .NET 1.1:

       /// 
 /// Static helper class.  Allows items to be retrieved from the Cache, 
 /// as well as allowing items to be dynamically set into the Cache via a Delegate.
 /// 
 public sealed class WebCacheHelper
 {
  /// 
  /// Delegate signature for getting items from the Cache.
  /// 
  public delegate object GetCacheItem();

  // Need private constructor to make the class static
  private WebCacheHelper()
  {
  }

  /// 
  /// Tries to get an item from the Cache, based on the specified cacheKey.  If the
  /// specified item does not exist, it then executes the delegate's method and places 
  /// the resulting object into the Cache. 
  /// 
  /// The key for the item in the Cache.
  /// A method that conforms to the GetCacheItem public delegate's signature.
  /// The object from the Cache or the object returned from the delegate method.
  public static object GetItemFromCache(string cacheKey, GetCacheItem method)
  {
   return GetItemFromCache(null, cacheKey, method);
  }

  /// 
  /// Tries to get an item from the Cache, based on the specified cacheKey.  If the
  /// specified item does not exist, it then executes the delegate's method and places 
  /// the resulting object into the Cache. 
  /// 
  /// The file to use in order to create a CacheDependency.
  /// The key for the item in the Cache.
  /// A method that conforms to the GetCacheItem public delegate's signature.
  /// The object from the Cache or the object returned from the delegate method.
  public static object GetItemFromCache(string dependencyFileName, string cacheKey, GetCacheItem method)
  {
   // Try to get the item from the Cache
   object item = HttpContext.Current.Cache[cacheKey];
   // See if it was not there
   if (item == null)
   {
    // It was not there, so get the object by executing the delegate
    item = method();
    // See if a file dependency was passed in
    if (dependencyFileName != null)
    {
     // There was a file dependency passed in, so insert into the Cache using the file dependency
     HttpContext.Current.Cache.Insert(cacheKey, item, new System.Web.Caching.CacheDependency(dependencyFileName));
    }
    else
    {
     // There was no file dependency passed in, so just add the item to the Cache
     HttpContext.Current.Cache[cacheKey] = item;
    }
   }
   return item;
  }
 }
As you can see from the comments in the class, the main method in the class is GetItemFromCache, and its job is to check for an item in the cache, 
and if the item is not there, to execute the delegate’s method, and then place the results into the cache.  The big drawback to using this algorithm 
is that the delegate method cannot have any arguments.  Other drawbacks are that it is hard-coded to the ASP.NET cache, as well as always returning 
a System.Object.  Here is a new version of this class, which is spruced up a great deal in .NET 2.0 via Generics and Anonymous Methods:

public static class CacheHelper
    {
        private static ICache cache;

        /// 
        /// Static constructor.
        /// 
        static CacheHelper()
        {
            // get the cache from the factory 
            CacheHelper.cache = CacheFactory.GetCache();
        }

        public delegate object FillCacheMethod();

        /// 
        /// Retrieves an item from the cache or executes the method and adds it to the cache.
        /// 
        /// 
        /// 
        /// 
        /// 
        public static T GetItemFromCache(string cacheKey, FillCacheMethod method)
        {
            T item;

            // if the cache was found.
            if (CacheHelper.cache != null)
            {
                item = CacheHelper.cache.Get(cacheKey);
                if (item == null)
                {
                    item = (T) method();
                    CacheHelper.cache.Add(cacheKey, item);
                }
            }
            else // if the cache wasn't found, just execute the method.
            {
                item = (T) method();
            }

            return item;
        }

        /// 
        /// Removes an item from the cache.
        /// 
        /// 
        public static void RemoveItemFromCache(string cacheKey)
        {
            CacheHelper.cache.Remove(cacheKey);
        }

        /// 
        /// Checks to see if an item is in the cache.
        /// 
        /// 
        /// True if the item is in the cache.
        public static bool Contains(string cacheKey)
        {
            return CacheHelper.cache.Contains(cacheKey);
        }
    }

As you can see, the class is much more flexible, and is strongly-typed (thanks to Generics).  Also, the delegate defined 
allows any number of arguments to be passed in through the use of Anonymous Methods.  It also is not tied to the 
ASP.NET Cache object, but rather, it uses the ICache interface (not shown), and it uses a 
CacheFactory (hey, I need to get one of those :) ) class (not shown either) to build the right instance of ICache based 
on configuration.  Many thanks to my co-worker Joel Rumerman for asking for this on a project and then helping to 
design and implement it!
You can find the code for this solution here.

posted on Wednesday, October 04, 2006 7:32 PM