Jonathan George's Blog

Optimising an ASP.NET MVC web site, part 4: Output caching in the brave new world of MVC

Posted in Technical Stuff by Jonathan on November 3, 2009

Note: This was originally posted on my old blog at the EMC Consulting Blogs site.

This is the penultimate part in a series of posts on optimisation work we carried out on my last project, www.fancydressoutfitters.co.uk – an ASP.NET MVC web site built using S#arp Architecture, NHibernate, the Spark view engine and Solr. Please also take the time to read parts 1, 2 and 3 for the full picture.

Output caching is something I’ve used extensively on previous ASP.NET WebForm projects, and I was surprised when I found out that it’s relatively immature in ASP.NET MVC. The core library contains the OutputCache filter, which does the same under the covers as the @OutputCache declaration in ASP.NET Web Forms. However, caching a full page is something that rarely suits in the current climate of dynamic and personalised websites – for example (and in common with most ecommerce websites) our site includes a summary of the user’s basket on every page. In the world of web forms, I’d use the asp:substitution control to implement donut caching, or implement fragment caching. Unfortunately neither of those options exist in the brave new world of ASP.NET MVC (if you’re a Java/Ruby/etc developer… stop sniggering, we’re catching you up 🙂 )

So there are a few ways round this that I’m aware of. The first involves using the partial views to do something similar to the old style ASP.NET Fragment Caching approach. In the WebForms world, this is implemented by splitting your page up into multiple user controls, and applying output caching as appropriate to the individual controls. Here’s a pretty basic example of how a product page on an ecommerce website might be laid out.

Page layout

The boxes coloured blue, green and purple can all be cached – albeit with different cache lifetimes and keys. The red box, which displays my user  basket total is unlikely to be cachable – at least not at the rendering level. Using the classic ASP.NET fragment caching approach, each of my coloured boxes would be implemented as a separate user control with appropriate OutputCache directives set. The ASPX page itself would contain very little other than the instances of these controls.

So how does this translate into MVC? It turns out that it’s pretty much the same. You create a number of partial views and add an OutputCache directive to their markup. Phil Haack covers the approach in this blog post. There’s just one downside: that approach only works with the standard WebForms view engine, and we’re using Spark, so we can’t go down this route.

I did hear a suggestion that making use of the RenderAction method from ASP.NET MVC 1.0 Futures could be the way forward. Essentially, each of my coloured boxes from the diagram would end up corresponding to a separate controller action, each of which would have an OutputCache action filter applied. These would then be pulled together by a “dumb” controller action whose corresponding view had multiple calls to Html.RenderAction to compose the chunks of the view together.

On the face of it – and assuming you were willing to accept the overhead involved in repeatedly invoking the full MVC request pipeline – it would work. However, there has been mention of a bug with MVC 1.0 which causes the OutputCache filter on an action method called with RenderAction to be applied to the overall request, not just the chunk being dealt with. This kind of thing is probably why the RenderAction method was bumped into MVC Futures instead of being shipped as part of the core MVC 1.0 release.

Phil Haack blogged something else that didn’t quite make it to MVC 1.0 which looked good on the surface. Essentially, it’s an HtmlHelper extension that hooks into the API used by the asp:substitution control. However, I had a bit of a headache in trying to work out how to use it. The problem is that within the callback you supply to the Substitute method, you don’t have access to your ViewData – not a massive surprised once you understand how the post-cache substitution mechanism in ASP.NET works. This means that you need to code some other method – which is going to be a static method you’ve stashed alongside your controllers – that will do all the necessary work, pulling bits of data out of the supplied HttpContext and returning a string to be dumped directly into the view.

There’s no doubt that this would work, and with some thought could be done without losing the testability and separation of concerns that makes the MVC pattern great. However, it’s not an ideal approach for me because it does mean that the pattern and conventions are broken to support what’s essentially an optimisation step. Because of this it will make it harder for people to see what’s going on in the code. I’ve already covered the caching we applied in the service layer; to me, output caching should be contained within the View Engine layer and should not leak beyond that. After all, there’s nothing else in my controller layer or below that couples my code to Spark, so I have no desire to introduce something that does.

Fortunately it turns out that the 1.1 version of the Spark view engine will contain pretty comprehensive output caching. This isn’t part of the v1.0 release, but has been in the development builds for several months now and is stable. It’s absolutely perfect for what I wanted as it allowed me to implement output caching with very few changes outside the views.

Unlike the ASP.NET WebForms fragment caching approach, you aren’t required to split your view into partials – you simply add <cache> elements around the sections of the page you want to cache. These sections can be dependent on different keys and cached for different durations, and there’s also a signalling mechanism that allows cached content to be expired on demand. In our case, we had already tackled the issue of cache keys for a ViewModels when we looked at caching in the controller layer, so it was a simple matter to use these same cache keys to control our output caching.

Spark also contains something called the ValueHolder which effectively allows you to defer the gathering of model data until it’s needed. This means that rather than build up the model for every request, only to pass it to a view which doesn’t need it because it’s entirely output cached, you can build your model using ValueHolder objects containing lambda functions that will only be executed if the data is needed. This seems like an interesting approach, but it’s not one I explored in detail because the caching we’d already implemented on the controllers made it less relevant.

One of my major hopes, which was unfortunately not realised, was that we’d be able to connect Spark’s output caching service to our distributed cache, Velocity. This would further reduce the workload across the web farm because it would mean that once a page was served from one webhead, it would be available pre-built to all of the others. However the output caching mechanism in Spark places unserializable objects into the cache, making it difficult to use with an out-of-process caching mechanism. This is a shame but by no means a deal breaker.

I’ve seen a few discussions around the value of output caching in the MVC world, with some saying that because the views are essentially just writing text to a stream, there is little gain to be had from caching. On a purely subjective level, the output caching did seem to make the site faster. It’s difficult to be sure because there is no way of enabling/disabling output caching via config in Spark, so it’s not easy to do comparative tests in a realistic environment. I can see the argument, and I’d certainly agree that out of the different focus areas output caching made the least difference to overall performance, but I believe it did make a difference and for the minimal effort involved in implementing it, was worth it.

In the final section, I’ll talk about my views on optimising this MVC based site compared to my experiences in the WebForms world, and share some hard numbers gained from our performance testing.

@jon_george1

Using an alternative ITempDataProvider implementation in ASP.NET MVC

Posted in Technical Stuff by Jonathan on October 14, 2009

Note: This was originally posted on my old blog at the EMC Consulting Blogs site.

As I’ve previously mentioned, we’re using Microsoft Velocity as the caching provider on my current project. One of my recent tasks was to create a new implementation of ITempDataProvider that stored data in the Velocity cache instead of Session State.

Now, I’m aware that a potentially faster implementation would have been to use the Velocity Session State provider, but when I tried this I hit a few snags. Basically, when you use Velocity directly, objects you add to the cache are serialized using the DataContractSerializer from the System.Runtime.Serialization namespace. This is nice because it means you don’t need to mark all your objects with the Serializable attribute, or start implementing ISerializable – as described here, the DataContractSerializer is capable of serializing objects of any public type that has a parameterless constructor. However, when you use Velocity’s session state provider, the framework takes over and you uses the more restrictive BinaryFormatter from the System.Runtime.Serialization.Formatters.Binary namespace. This does require you to explicitly make your objects serializable, which is a bit of a pain.

To avoid going through and marking the relevant classes serializable, I took the plunge and knocked up an ITempDataProvider implementation using Velocity for my project. It was actually a very straightforward task, but once I’d done that my next issue was to work out how to get it into my controllers.

The examples I’ve seen online suggest one of two approaches:

  1. Initialise it in either the constructor or the Initialize() method of the base controller.
  2. Set it in the controller factory.

Option 1 would look something like this:

  1. public BaseController()
  2. {
  3.     this.TempDataProvider = new VelocityTempDataProvider();
  4. }

Simple enough, however all my controllers would instantly be tightly coupled to the VelocityTempDataProvider. Ok, so in my tests I could set the TempDataProvider property to a mock straight after creation of the controller, but it’s still not nice.

I could do this:

  1. public BaseController()
  2. {
  3. }
  4. public BaseController(ITempDataProvider tempDataProvider)
  5. {
  6.     this.TempDataProvider = tempDataProvider;
  7. }

Which would also work, although it would require all derived controllers to pass the required instance of ITempDataProvider through from their own constructors, requiring me to modify all of the derived classes. Or there’s this:

  1. public BaseController() : this(new VelocityTempDataProvider())
  2. {
  3. }
  4. public BaseController(ITempDataProvider tempDataProvider)
  5. {
  6.     this.TempDataProvider = tempDataProvider;
  7. }

Which will default all controllers to use the Velocity temp data provider unless they specify an alternative.

Ultimately though, I don’t like the fact that Option 1 requires selecting the correct ITempDataProvider implementation in the controllers themselves. All of these methods would work, but setting the TempDataProvider property is a responsibility that fits better in the controller factory than the controller itself. The factory is responsible for building the controller; it should be responsible for setting up any dependencies at the same time.

We’re using the WindsorControllerFactory from MvcContrib to allow Castle Windsor to create our controllers and automagically resolve their dependencies for us. As part of our application startup, we execute this code:

  1. ControllerBuilder.Current.SetControllerFactory(new WindsorControllerFactory(this.container));

This is what tells the MVC framework to use the Windsor controller factory. So what’s the best way of getting my custom temp data provider in there? I could subclass the WindsorControllerFactory and override the relevant methods. But using a custom temp data provider isn’t something I’d only ever want to do with WindsorControllerFactory – it could equally apply to the default controller factory, or to any other.

It’s a scenario that could be custom made for what I think is my favourite Gang of Four design pattern: decorator. This pattern allows you to compose additional responsibilities onto an object without needing to subclass it. If you’re not familiar with it, I’d suggest a read of this chapter from the Head First Design Patterns book.

Here’s what my decorator looks like:

  1. public class ControllerFactoryCustomTempDataProviderDecorator : IControllerFactory
  2. {
  3.     private readonly IControllerFactory decoratedControllerFactory;
  4.     private readonly IWindsorContainer windsorContainer;
  5.     public ControllerFactoryCustomTempDataProviderDecorator(IControllerFactory decoratedControllerFactory, IWindsorContainer container)
  6.     {
  7.         this.decoratedControllerFactory = decoratedControllerFactory;
  8.         this.container = container;
  9.     }
  10.     public IController CreateController(RequestContext requestContext, string controllerName)
  11.     {
  12.         var controller = this.decoratedControllerFactory.CreateController(
  13.             requestContext,
  14.             controllerName);
  15.         return this.SetTempDataProvider(controller);
  16.     }
  17.     public void ReleaseController(IController controller)
  18.     {
  19.         this.decoratedControllerFactory.ReleaseController(controller);
  20.     }
  21.     private IController SetTempDataProvider(IController controller)
  22.     {
  23.         var concreteController = controller as Controller;
  24.         
  25.         if (concreteController != null)
  26.         {
  27.             concreteController.TempDataProvider = this.windsorContainer.Resolve<ITempDataProvider>();
  28.         }
  29.         return controller;
  30.     }
  31. \

And here’s how it’s used:

  1. var windsorContollerFactory = new WindsorControllerFactory(this.container);
  2. var decoratedControllerFactory =
  3.     new ControllerFactoryCustomTempDataProviderDecorator
  4.         (windsorContollerFactory,
  5.         this.container);
  6. ControllerBuilder.Current.SetControllerFactory(decoratedControllerFactory);

So now all my controllers will have their TempDataProvider property set at the time of creation. As an added bonus, the specific implementation of ITempDataProvider to use is also resolved using the Windsor container making it easily configurable.

I’m a lot happier with the design I’ve ended up with – it’s more flexible and reusable and it doesn’t annoy me every time I see it. Of course, any of the other methods I mentioned would have worked, and other people might prefer them, but I hope this has given you a few options to consider if you need to do something similar.

Update (later that same day…)

Writing this up must have re-awoken some thought processes because as I was making my way home I started wondering again about my final solution. My thought process went something like this:

  1. I created a decorator with the sole purpose of setting a property of my controllers. Basically, it’s job is to inject a dependency into a newly created object.
  2. I am using a dependency injection tool (Castle Windsor) to create my controllers.
  3. Why am I not using my dependency injection tool to inject my dependency?

Why indeed?

Ever since I started using Windsor, I’ve used constructor injection for dependencies – that way you don’t have to tell it which dependencies you need it to resolve, it will just do it’s best to satisfy all the constructor arguments from the types it has registered. But like all good IoC containers, Windsor also supports property setter injection – essentially, I can tell it to set properties on newly created objects to whatever I want them set to. (See this article for more details.)

So the decorator I was happy with last week is now redundant. That doesn’t mean it wouldn’t ever be useful – it could easily be used with the standard ASP.NET MVC controller factory, for example. However, for any controller factory that uses an IOC container it is probably not needed.

@jon_george1