Skip to main content

Caching example with DynamicProxy

April 04, 2012 mgroves 0 Comments
Tags: caching DynamicProxy StructureMap IoC

Here's a quick example of how to write a caching aspect with Castle DynamicProxy.

First, let's write an implementation and a service worth caching:

public interface IMyService
{
string GetSomeLongRunningResult(string input);
}
view raw IMyService.cs hosted with ❤ by GitHub
public class MyService : IMyService
{
public string GetSomeLongRunningResult(string input)
{
Thread.Sleep(5000); // simulate long running process
return string.Format("Result of '{0}' returned at {1}", input, DateTime.Now);
}
}
view raw MyService.cs hosted with ❤ by GitHub

Next, you need to decide where to cache the results. If you're using a web app, maybe try ASP.NET's Cache object. If you're writing an Azure app, try AppFabric caching. You can cache things in a database, a text file, whatever is appropriate for your application.

For this simple example, I'm going to cache everything in memory, in a static Dictionary object. Also, nothing that goes into my cache will ever expire or be invalidated. Once it's cached, it's cached forever. Also, it's not thread safe. Not a very useful cache in a real app, so let me just be clear: do not use this in a production application. It's only for demonstration: whatever you use for caching is up to you, I'm just demonstrating the aspect part.

// this is not a class you should actually use in production!
// this is just caching things in a static dictionary
// and individual items don't ever expire
// you should use a cache that suits your application, like ASP.NET's Cache class, AppFabric caching, etc.
public class StaticCachingInterceptor : IInterceptor
{
static readonly IDictionary<string, object> _cache;
static StaticCachingInterceptor()
{
_cache = new Dictionary<string, object>();
}
public void Intercept(IInvocation invocation)
{
var cacheKey = GenerateCacheKey(invocation.Method.Name, invocation.Arguments);
if (_cache.ContainsKey(cacheKey))
{
invocation.ReturnValue = _cache[cacheKey];
return;
}
invocation.Proceed();
_cache[cacheKey] = invocation.ReturnValue;
}
static string GenerateCacheKey(string name, object[] arguments)
{
if (arguments == null || arguments.Length == 0)
return name;
return name + "--" + string.Join("--", arguments.Select(a => a.ToString()).ToArray());
}
}

I'm generating the cache key by appending the argument values to the method name. So if you call MyMethod("test") and MyMethod("test2"), that's two different keys that will cache two different results. This might work for you, or you might need a more complex GenerateCacheKey method (something that uniquely identifies objects, perhaps).

Next, I wire up my IoC container to return MyService when asked for an implementation of IMyService. But I also have to make sure that my IoC container (StructureMap in this case) applies the caching interceptor to it.

ObjectFactory.Configure(x =>
{
var proxyGenerator = new ProxyGenerator();
x.For<IMyService>().Use<MyService>()
.EnrichWith(y => proxyGenerator.CreateInterfaceProxyWithTarget<IMyService>(y, new StaticCachingInterceptor()));
});
view raw gistfile1.cs hosted with ❤ by GitHub

I put it all together in a simple console app:

var myService = ObjectFactory.GetInstance<IMyService>();
Console.WriteLine("[{0}] Now getting the result with argument 'foo' for the first time...", DateTime.Now);
Console.WriteLine("[{0}] {1}", DateTime.Now, myService.GetSomeLongRunningResult("foo"));
Console.WriteLine("[{0}] Now getting the result with argument 'foo' for the second time...", DateTime.Now);
Console.WriteLine("[{0}] {1}", DateTime.Now, myService.GetSomeLongRunningResult("foo"));
Console.WriteLine("[{0}] Now getting the result with argument 'bar' for the first time...", DateTime.Now);
Console.WriteLine("[{0}] {1}", DateTime.Now, myService.GetSomeLongRunningResult("bar"));
view raw Program.cs hosted with ❤ by GitHub

And here's the result:

Console app with caching

Comments

Matthew D. Groves

About the Author

Matthew D. Groves lives in Central Ohio. He works remotely, loves to code, and is a Microsoft MVP.

Latest Comments

Twitter