The latests release of LazyCache, my open source cache library based on ObjectCache, makes it easy to cache the results of asynchronous or Task returning methods, so now it is simpler for you to speed up your application.

Why bother with Async?

Async code tends to be more efficient as you release threads while they are waiting for the response from an asynchronous resource, but at a cost of added code complexity.

To help this .Net 4 introduced the Task based programming model which is far easier to work with than previous asynchronous programming constructs such as threads, events, semaphores and delegates. However the code you produced is still very different to the synchronous version.

.Net 4.5 introduced the async/await operators which is really just combination of a compliler optimisation to save you implementing a state machine and make async code look synchronous and some more developer friendly Task APIs.

Async programming is still harder than synchronous in dot net, but because of the benefits DoSomeThingAsync methods are becoming increasingly common, and often now library writers make it the only way.

The most important thing to remember is that you need to be async all the way down - from program entry to completion - so in asp.net use async controller actions, and in other dot.net apps ensure your call stack starts with an async method or a new task.

What should I cache (asynchronously)?

Caching makes the most sense for anything that takes time/effort/resource to produce such as the result of a HTTP api call you make often, or the results of a large SQL query.

For example, imagine you had a method like below.

private async Task<ProductDetails> GetProductAsync(int productId)  
{
     //go to db or send api request or whatever async
     ...
     await productClient.GetAsync(productId);
}

Roll your own?

If you could place the ProductDetails object in a dictionary keyed by id once the task completed then you could fetch it out next time and skip making the async call. However, you have to write non-trivial code to do so (read up on the cache aside pattern for more info), it is easy to make a mistake with async/tasks causing bugs and deadlocks, and a second request for the same product in quick succession would not wait for the first request to complete, leading to unnecessary async calls.

What if this was not async?

Caching the results of synchronous calls is easy - we just save the result in memory. And it turns out that if you use LazyCache the hard work of unwrapping the task, caching the result and ensuring the task only runs once, is all done for you so caching async calls is very similar to synchronous ones.

To add the LazyCache one-liner

Given your existing code looks like this:

// async call without caching
var product = await GetProductAsync(99);  

Then to add caching change it to this:

// async call with caching
var product = await cache.GetOrAddAsync(  
        "product-99", () => GetProductAsync(99);

As you can see the pattern is very similar to using Lazycache with non async code, just remember to await the cache call and to use the Async suffix version of the GetOrAdd method.

What are the benefits?

  • Trivial to implement caching of async results
  • Non-blocking/async all the way down
  • Built in lazy locking so duplicate calls wait for the first one to return
  • Same style as synchronous lazy caching

Any downsides?

  • You cannot serialise a Task so alternative ObjectCache providers such as Redis will not work. MemoryCache is your only option.

Sync or Async?

So it should now be simpler to add caching to any dot net app:

  • If it is synchronous use IAppCache.GetOrAdd(...)
  • If it is async use await IAppCache.GetOrAddAsync(...)

Happy caching.

More info: