DynaCache- just like page output caching, but for classes

Share on:

Anyone who has done any serious work with any ASP.NET based framework will know that page output caching is a great feature. For those not familiar with it, the basic premise is that it makes sure that the generation of content is done only once for a set of parameters, and that all subsequent requests with the same parameters are served up from the cache for a specified period of time.

Wouldn’t it be nice if you could do a similar thing for methods on classes, just by applying an attribute like this?

1
2[CacheableMethod(30)]
3public virtual string GetData(int id)

Thanks to a little library called DynaCache, you can!

How does it work?

Say you have a class called TestClass:

image

The LoadData method is marked as virtual, and has an attribute called CacheableMethod applied to it, indicating the number of seconds the results should be cached for.

The CachableMethod attribute is the first interaction with the DynaCache framework - the second is with a class called Cacheable:

image

Calling Cacheable.CreateType() at runtime creates a new class called CacheableTestClass, deriving from TestClass and overriding any methods with the CachableMethod attribute, resulting in a class hierarchy like this:

image

This new class is created using Reflection.Emit and only exists in memory, so you can’t use a reflector-like program to see what’s generated, but if you could it would look something like this:

 1
 2public class CacheableTestClass : TestClass
 3{
 4    private IDynaCacheService cacheService;
 5    
 6    public TestClass(IDynaCacheService cacheService)
 7    {
 8        this.cacheService = cacheService;
 9    }
10
11    public override string LoadData(int id)
12    {
13        string cacheKey = String.Format(CultureInfo.InvariantCulture, "TestClass_LoadData(Int32).{0}", id);
14        object result;
15        if (!this.cacheService.TryGetCachedObject(cacheKey, out result))
16        {
17            result = base.LoadData(id);
18            this.cacheService.SetCachedObject(cacheKey, result, 200);
19        }
20        
21        return (string)result;
22    }
23}

Notice the IDynaCacheService parameter in the constructor? That’s the third piece of the DynaCache framework - an instance of a class capable of interacting with whatever backing cache is being used. Out of the box DynaCache includes a concrete implementation of this called MemoryCacheService - it’s just a wrapper around a .NET 4 MemoryCache instance. There’s no reason why you shouldn’t create your own though, e.g. for the ASP.NET or Windows Azure cache.

Making it simple with dependency injection

What this all means is that at runtime you need to be using CacheableTestClass rather than TestClass, otherwise all the generated caching code will never be used. Although it’s possible to construct and use these types yourself, the simplest and best way to do that is to use a dependency injection framework, such as Ninject, StructureMap, etc.

For the sake of illustration, I’m going to use Ninject. The original configuration would have simply mapped ITestClass to TestClass, like this:

1
2kernel.Bind<ITestClass>().To<TestClass>();

Using DynaCache is only marginally more complicated, you just configure your kernel like this:

1
2kernel.Bind<IDynaCacheService>().To<MemoryCacheService>();
3kernel.Bind<ITestClass>().To(Cacheable.CreateType<TestClass>());

The first line configures the cache service to pass to instances of CacheableTestClass*,* whilst the second binds ITestClass to the cacheable version of TestClass.

That’s all there is to it! Now every time an instance of ITestClass is required, Ninject will construct and return an instance of CacheableTestClass - the rest of your code that consumes ITestClass will automatically make use of the dynamically constructed caching code.

Where to get DynaCache

You can get it from the CodePlex project site, or you can install it into your project using the Nuget command:

1
2Install-Package DynaCache

Summary

Hopefully all the detail hasn’t put you off - this whole article essentially boils down to just these three steps:

  1. Make the methods overridable and apply the CacheableMethod attribute to them.
  2. Configure your DI framework to return an instance of IDynaCacheService, either MemoryCacheService or one you have implemented yourself.
  3. Configure your DI framework to return the result of Cacheable.CreateType for your type.

I’d be really interested in feedback for DynaCache - let me know your thoughts in comments below, or on the project discussion board.