The typical procedure to work with cache is
1.to check if item for specified key exist in a cache;
2.if yes, return it;
3.if no, create it (e.g load from database),
4.save to the cache
5. and then return it.
Some developers use the code like the following:
If (DataCache.GetCache["key"] == null)
{ // code for loading object
...
DataCache.SetCache("key", obj);
}
return DataCache.GetCache["key"]; //Not good, can cause problems (at least in ASP.Net 2.0).
Sometimes SetCache is not effected immediately(or expired immediately) and subsequent GetCache returns null.
Back in 2006 I lost a lot of time trying to debug the problem in DotNetNuke "DataCache.GetCache usage pattern causes random Null reference exceptions".
Now in a different application I noticed the same unreliable pattern.
To avoid mistakes and make process of getting objects from cache consistent, I've created a Generic function with delegate as a parameter to implement the safe pattern.
using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
public static class CacheHelper
{
public delegate T CreateInstanceDelegate<T>();
public static T LoadFromCache<T>(String cacheKey, CreateInstanceDelegate<T> dlgt) where T:class
{
T coll = HttpRuntime.Cache[cacheKey] as T;
if (coll == null)
{
coll = dlgt() as T;
if (coll != null)
{
HttpRuntime.Cache[cacheKey] = coll;
}
}
return coll;
}
public static string GetFileFromHttpServerToCache(String url,bool throwIfNotFound)
{
//TODO: alternatively add file dependancy
//System.Web.HttpContext.Current.Cache.Add(cacheKey, contents, new CacheDependency(fileName), DateTime.MaxValue, TimeSpan.FromHours(2), CacheItemPriority.Normal, null);
String content = CacheHelper.LoadFromCache<String>(url, delegate
{
string filePath= HttpContext.Current.Server.MapPath(url);
if (throwIfNotFound == false && !File.Exists(filePath))
{
return "";
}
string sContent = StreamHelper.FileToString(filePath);
return sContent;
});
return content;
}
public static string GetFileFromHttpServerToCache(String url,bool throwIfNotFound)
{
//TODO: alternatively add file dependancy
//System.Web.HttpContext.Current.Cace.Add(cacheKey, contents, new CacheDependency(fileName), DateTime.MaxValue, TimeSpan.FromHours(2), CacheItemPriority.Normal, null);
String content = CacheHelper.LoadFromCache<String>(url, delegate
{
string filePath= HttpContext.Current.Server.MapPath(url);
if (throwIfNotFound == false && !File.Exists(filePath))
{
return "";
}
string sContent = StreamHelper.FileToString(filePath);
return sContent;
});
return content;
}
}
MyClass entity =
CacheHelper.LoadFromCache<MyClass >(sCacheKey, delegate
{
return LoadFromDb(localParameters);
});