RIA Services Output Caching

It has come to my attention that we haven’t published any good resources on an important feature of RIA Services – our integration with ASP.NET output caching. This is a shame, since output caching can greatly improve the performance of your application. This post will explain the support we offer and hopefully draw more attention to the feature.

In RIA Services, declaring that the results of a query operation can be cached is as simple as applying our OutputCacheAttribue (System.ServiceModel.DomainServices.Server) to your query method:

image

For the above query method, an absolute expiration of one hour is set for the returned product list. Once your application has retrieved the results, subsequent calls to GetProducts from the client will result in cache hits – your GetProducts query method will not be invoked until the cached result expires (in one hour). If you take a look at OutputCacheAttribue you’ll see that you can use it for additional caching strategies: you can specify different locations for cache, set up sliding expirations, use cache profiles, cache dependencies, etc. 

RIA Services isn’t performing any magic here – we’ve simply given you an easy declarative way to specify your cache requirements. The cache directive you specify on your query method will be translated into the corresponding HttpCachePolicy in our service layer. This takes place in our custom query operation invoker (WCF IOperationInvoker). Before invoking your query method, we inspect the cache directives you’ve applied, and configure HttpContext.Response.Cache appropriately.

You can use the sample application I’ve attached to this post to experiment with the feature. Each time the “Load Products” button is hit, the client issues a query to load some products data. You’ll see that the elapsed time for the entire request is displayed:

image

As you can see, without caching the request takes about 3 seconds. Applying the attribute [OutputCache(OutputCacheLocation.Server, duration: 10)] to your method and rerunning the app, you’ll see that after the first request, the average response time goes down to around 50 ms since cached results are being returned:

imageimage

You’ll also see that a breakpoint in the query method will not be hit before the cached result expires. After the absolute expiration of 10 seconds, the next time the client issues a request your query method will again be called to retrieve fresh results which will again be cached. Finally, to demonstrate client side caching, if you change the attribute to [OutputCache(OutputCacheLocation.Client, duration: 10)] and inspect the network traffic with Fiddler, you’ll see that after the initial request subsequent loads will not even hit the server (the results are cached in the browser). As before, once the cache expiration of 10 seconds is reached, the browser cache entry will expire.

imageimage

A few additional notes:

  • Caching is only enabled for query methods, and only if they use GET (i.e. QueryAttribute.HasSideEffects = false, which is the default)
  • Caching is disabled for requests that specify an additional LINQ query. This is because output caching should generally only be used for a controlled set of inputs, to ensure that a small number of responses are cached. Otherwise it would be easy for someone to fill up server memory by sending a bunch of query variations.
  • For query methods taking parameters (e.g. GetProductsByCategory(int categoryID), we automatically set up the HttpCachePolicy to vary by those parameters (HttpCachePolicy.VaryByParams).

In summary, through intelligent use of caching the performance of your application can be greatly improved. For example, if you have query methods that return reference data that doesn’t change very often, it likely makes sense to cache that. RIA Services builds on the ASP.NET cache infrastructure, so the guidelines and best practices established for ASP.NET applications generally apply.

OutputCaching.zip