We’ve started implementing some caching code in our MVC project. We’ve derived from .Net’s ObjectCache to store it in a SQL database.
What we do right now is in each method we want to cache we have code like this:
public StatusCounts GetCounts()
{
var cache = new SqlCache<StatusCounts>("mykey");
if(cache.HasCachedData())
return cache.GetCachedData();
var data = StatusRepository.GetCounts();
cache.SetData(data, DateTime.Now.AddHours(2));
}
As far as caching goes, it works. But my concern is it seems like now GetCounts has multiple responsibilities. It’s no longer just calculating counts, it’s getting them from a cache (if they exist) and saving them to a cache before returning.
Is there a cleaner way to do this?
2
You can separate the cache initialization from returning the value, and implement the cache initialization in a generic way:
public StatusCounts GetCounts()
{
var cache = GetInitializedCache<StatusCounts>("mykey",StatusRepository.GetCounts,2);
return cache.GetCachedData();
}
private SqlCache<T> GetInitializedCache<T>(string key,Function<T> getData,double hours)
{
var cache = new SqlCache<T>(key);
if(!cache.HasCachedData())
{
T data = getData();
cache.SetData(data, DateTime.Now.AddHours(hours));
}
return cache;
}
That will separate the concerns of caching and calculating the values as well as giving you a reusable function. Of course, the method body of GetCounts
is still aware of the caching mechanics, and if you want to avoid that too, @Peri’s answer maybe the right approach (for the price of having to introduce a non-trivial third-party component).
2
I would try to implement it using AOP. You could try Castle.Windsor’s Interceptors or mechanism other IoC container have. That way you implement caching in one place only (Interceptor) and there is no sign of cache in your method.
http://docs.castleproject.org/Default.aspx?Page=Interceptors&NS=Windsor&AspxAutoDetectCookieSupport=1
10
I built a little tool for this some time ago, https://code.google.com/p/mbcache, that we use with success in our shop. It uses AOP the way @Peri describes behind the scenes.
No matter using this or another tool, I would defiantly favor AOP for this. As you write yourself, you shouldn’t mix responsibilities within your classes (extract caching logic to a seperate method don’t matter IMHO – it just moves the dependency instead of getting rid of it).