Do you need a quick and cache wrapper class? Here’s a static class which I included in my more recent C# web application. You’ll notice the class uses generics to allow for some, umm, generic functionality.
public static class CacheHelper { /// <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> public static void Add<T>(T o, string key) where T : class { // NOTE: Apply expiration parameters as you see fit. // In this example, I want an absolute // timeout so changes will always be reflected // at that time. Hence, the NoSlidingExpiration. HttpContext.Current.Cache.Insert( key, o, null, DateTime.Now.AddMinutes( ConfigurationHelper.CacheExpirationMinutes), System.Web.Caching.Cache.NoSlidingExpiration); } /// <summary> /// Remove item from cache /// </summary> /// <param name="key">Name of cached item</param> public static void Clear(string key) { HttpContext.Current.Cache.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 HttpContext.Current.Cache[key] != null; } /// <summary> /// Retrieve cached item /// </summary> /// <typeparam name="T">Type of cached item</typeparam> /// <param name="key">Name of cached item</param> /// <returns>Cached item as type</returns> public static T Get<T>(string key) where T : class { try { return (T) HttpContext.Current.Cache[key]; } catch { return null; } } }
And here is a relatively standard sample usage of the library.
public override List<Employee> GetEmployeeList() { string key = ConfigurationHelper.CacheKeyEmployeeList; List<Employee> employees = CacheHelper.Get<List<Employee>>(key); if (employees == null) { employees = instance.GetEmployeeList(); CacheHelper.Add(employees, key); } return employees; }
Notice how I’m grabbing the cached value, storing it in a local variable and then checking if it is equal to null rather than using the CacheHelper.Exists() method. If I used the CacheHelper.Exists() method, the cached object could expire between the time I check its existence and the time I get its value through the CacheHelper.Get() method. Therefore, the above approach is the safest strategy to use when retrieving cached values. CacheHelper.Exists() should really only be used for quick existence checks which are unrelated to the fetch.
But if you want to use the code CORRECTLY there’s a catch. Did you notice the “class” constraint on the CacheHelper.Get() and CacheHelper.Add() methods? I did this because you can’t always return null from a generic method. If the return type were always a reference type it would be fine, but comparing a non-nullable value type to null would throw a runtime exception or would always evaluate to false. Therefore, I’ve constrainted CacheHelper which limits its functionality but unsure safe use of the cache. If you’re feeling dangerous, you’re welcome to remove the constraints.
I’ll update the library once I come up with a good work around. Speaking of, any suggestions?
Let me know if you have any questions and/or this type of post is helpful. Thanks.
UPDATE 12/10:
After further review, I’ve removed the “class” constraint from the CacheHelper. The Get() method now follows the common Try(x, out y) pattern where x is what you wish to operate on, y is the resulting value and the method return success or failure. Here’s a sample call:
string key = "EmployeeList"; List<Employee> employees; if (!CacheHelper.Get(key, out employees)) { employees = DataAccess.GetEmployeeList(); CacheHelper.Add(employees, key); Message.Text = "Employees not found but retrieved and added to cache for next lookup."; } else { Message.Text = "Employees pulled from cache."; }
And here’s the updated class. Note, I’ve also provided a sample project for download. Thanks for your comments!
using System; using System.Web; public static class CacheHelper { /// <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> public static void Add<T>(T o, string key) { // NOTE: Apply expiration parameters as you see fit. // I typically pull from configuration file. // In this example, I want an absolute // timeout so changes will always be reflected // at that time. Hence, the NoSlidingExpiration. HttpContext.Current.Cache.Insert( key, o, null, DateTime.Now.AddMinutes(1440), System.Web.Caching.Cache.NoSlidingExpiration); } /// <summary> /// Remove item from cache /// </summary> /// <param name="key">Name of cached item</param> public static void Clear(string key) { HttpContext.Current.Cache.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 HttpContext.Current.Cache[key] != null; } /// <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) HttpContext.Current.Cache[key]; } catch { value = default(T); return false; } return true; } }
December 11th, 2008 at 3:39 am
Cool looks like something i could use.
December 11th, 2008 at 10:25 pm
How about a bool TryGetValue(string key, out TValue value) method? As it follows the tryget pattern it would simplify retrieving values and running some code conditionally only if the value exists and is valid.
December 12th, 2008 at 9:29 am
Thanks for the suggestion, Waldek. I updated the class using this approach last night and it is much cleaner. Thanks for validating my second attempt. I’ll post the updated code shortly.
December 13th, 2008 at 6:19 am
You’re welcome. Thanks for sharing
January 4th, 2009 at 5:40 am
How can I determine the size of an item in cache?
January 4th, 2009 at 10:08 am
Great question. Try serializing the object to the file system. It’s physical file size is a rough estimate of what was in memory. If you want to start really digging into this subject, you may wish to start here: http://blogs.msdn.com/tess/arc.....17819.aspx. Good luck.
April 2nd, 2009 at 1:22 pm
Thanks for this.
April 2nd, 2009 at 1:49 pm
@Michael Washington You’re very welcome.
April 10th, 2009 at 8:09 am
I used the code in a module posted here:
http://dnnsilverlight.adefwebs.....fault.aspx
April 11th, 2009 at 11:58 am
@Michael Washington Thanks for sharing your site. You have some excellent demos and source code available. Good stuff.
June 8th, 2009 at 8:47 pm
Thanks for this.
I am just copied the code and posted in my blog. ^.^
July 14th, 2009 at 4:44 am
Great post! I’ll subscribe right now wth my feedreader software!
August 11th, 2009 at 12:58 pm
Thanks for the great post. Is it possible to add the OnRemove even to this class?
I want to call a function and repopulate the cache when it is removed.
August 15th, 2009 at 10:26 am
@Shuaib - Neat idea but this class is entirely static and it merely wraps the HttpContent cache object. An OnRemove event wouldn’t really work without some serious re-architecture. Thanks for the question.
September 14th, 2009 at 7:40 am
H! can i determine the size of each item in the cache?
I followed the linked but i can’t understand, what’s happening there..I mean where i can run the command..
Thanks..
November 26th, 2009 at 2:01 pm
Thanks a load. Really helpful!