Jonathan George's Blog

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

Follow

Get every new post delivered to your Inbox.