In a previous post I looked at basic dependency injection. In this post I demonstrated dependency injection using a simple example in which all functionality in the application was wrapped up in a single class (TimeWriter) with an associated interface (ITimeWriter). The application uses the dependency injection framework to get an instance of this class by its interface. In the process of doing this, all dependencies within the class are also satisfied. The application can then run as normal and write the date and time to the screen.
This approach is fine for console applications as they usually only have one entry point (the Main method) and from there we have complete control over the all code that runs once the application has started. Including dependency injection in ASP.NET MVC web-applications isn’t so straightforward as by default we don’t have control over how code runs right from the start of a user request. The MVC routing engine interprets the request, maps this to a controller, instantiates the controller (using the default controller factory) and calls the correct method on the controller instance. We then get to steer the code execution within the controller method. From a programming perspective, MVC development is like writting an application with multiple entry points contained in multiple controllers.
As we don’t get to instantiate the controller for ourselves there is no obvious way to include dependency injection, unless we write a class and interface for every controller operation and register them with the dependency injection engine in an analogous fashion to the console application example. This would work but it’s clearly not a good idea!
Fortunately ASP.NET MVC allows us to specify custom controller factories, and Castle Windsor includes classes to make it very simple to include dependency injection in this customisation process. Once configured, you can include dependencies as properties in your controllers which, along with the controllers themselves, are resolved by Castle Windsor each time a user request is received.
This time I’m starting with an Empty ASP.NET MVC application using the Razor view engine. The application and the containing solution are called ControllerInjection. Once created, I use NuGet to add a reference to Castle Windsor. My starting point thus looks like this:
As in my basic dependency injection post I’m going to use Watch and IWatch as my example dependencies:
using System;
namespace ControllerInjection
{
public interface IWatch
{
DateTime GetTime();
}
}
using System;
namespace ControllerInjection
{
public class Watch : IWatch
{
public DateTime GetTime()
{
return DateTime.Now;
}
}
}
These have been added to the solution as follows:
Now I’m going to include a controller called HomeController:
This contains the following code:
using System.Web.Mvc;
namespace ControllerInjection.Controllers
{
public class HomeController : Controller
{
public IWatch Watch { get; set; }
public ActionResult Index()
{
var text = string.Format("The current time on the server is: {0}", Watch.GetTime());
return Content(text);
}
}
}
The key point here is the property called Watch with type IWatch. This is the dependency which will be injected at runtime. To save having to write a new view the Index method returns raw content.
The next component to implement is our custom controller factory. I’ve included this in a folder called DependencyInjection:
The code inside ControllerFactory is as follows:
using System;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Castle.MicroKernel;
namespace ControllerInjection.DependencyInjection
{
public class ControllerFactory : DefaultControllerFactory
{
private readonly IKernel kernel;
public ControllerFactory(IKernel kernel)
{
this.kernel = kernel;
}
public override void ReleaseController(IController controller)
{
kernel.ReleaseComponent(controller);
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
{
throw new HttpException(404, string.Format("The controller for path '{0}' could not be found.", requestContext.HttpContext.Request.Path));
}
return (IController)kernel.Resolve(controllerType);
}
}
}
This class extends the DefaultControllerFactory, overriding the methods to get and release controllers. Note that the Castle Windsor kernel is used to resolve each controller by the type of controller required.
The next thing to add is a dependency injector:
using System.Web.Mvc;
using Castle.MicroKernel.Registration;
using Castle.Windsor;
using Castle.Windsor.Installer;
namespace ControllerInjection.DependencyInjection
{
public static class Injector
{
private static readonly object InstanceLock = new object();
private static IWindsorContainer instance;
public static IWindsorContainer Instance
{
get
{
lock (InstanceLock)
{
return instance ?? (instance = GetInjector());
}
}
}
private static IWindsorContainer GetInjector()
{
var container = new WindsorContainer();
container.Install(FromAssembly.This());
RegisterInjector(container);
RegisterControllers(container);
RegisterTimeComponents(container);
return container;
}
private static void RegisterTimeComponents(WindsorContainer container)
{
container.Register(
Component.For<IWatch>()
.ImplementedBy(typeof(Watch))
.LifeStyle.Singleton);
}
private static void RegisterControllers(WindsorContainer container)
{
var controllerFactory = new ControllerFactory(container.Kernel);
ControllerBuilder.Current.SetControllerFactory(controllerFactory);
container.Register(
Classes.FromThisAssembly()
.BasedOn(typeof(IController))
.If(t => t.Name.EndsWith("Controller"))
.If(t => t.Namespace.StartsWith("ControllerInjection.Controllers"))
.Configure(c => c.LifestylePerWebRequest()));
}
private static void RegisterInjector(WindsorContainer container)
{
container.Register(
Component.For<IWindsorContainer>()
.Instance(container));
}
}
}
This is very similar to the injector in my basic dependency example, apart from the addition of a method to register controllers. Crucially, this method also registers our customer controller factory. Note that the lifestyle of each controller is set using LifestylePerWebRequest. This ensures that each new request gets its own controller instance.
As the controller factory is only registered once the injector singleton instance has been instantiated, we need to make sure that we initialise the injector when the application starts up. I’ve done this in the Application_Start method of the Global.asax class:
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Routing;
using ControllerInjection.DependencyInjection;
namespace ControllerInjection
{
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
// Ensure the dependency injection has been initalised:
var injector = Injector.Instance;
/* Normally you would do something with the injector here, like writing an entry to
* the log stating that the application had started. */
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
}
}
}
In practice this doesn’t prove to be a problem as most applications will involve some kind of logging tool (such as Log4Net) which will be obtained from the dependency injection engine. It is usual to log the fact that the application starts, so the injector gets instantiated anyway.
Running the code results in the following string being written to the screen as expected:
The current time on the server is: 11/04/2013 21:59:36