Injecting dependencies in MVC3 with Windsor

In this blog post I will explain how to inject your dependencies into your controllers in a MVC 3 application with Castle Windsor.

First of all you need to create a WindsorInstaller. This class is responsible for the configuration of your IoC container.

WindsorInstaller.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
using System.Web.Mvc;
using Castle.MicroKernel.Registration;
using Castle.MicroKernel.SubSystems.Configuration;
using Castle.Windsor;
using Website.Services;
using Website.Controllers;
using Website.Repositories;

namespace Website.Infrastructure
{
public class WindsorInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(FindControllers().Configure(c => c.LifeStyle.Transient));
container.Register(Component.For<IProductRepository>().ImplementedBy<ProductRepository>().LifeStyle.Transient);
container.Register(Component.For<ICustomerRepository>().ImplementedBy<CustomerRepository>().LifeStyle.Transient);
container.Register(Component.For<ISomeWebService>().ImplementedBy<SomeWebServiceClient>().LifeStyle.Transient);
}

private BasedOnDescriptor FindControllers()
{
return AllTypes.FromThisAssembly()
.BasedOn<IController>()
.If(Component.IsInSameNamespaceAs<HomeController>())
.If(t => t.Name.EndsWith("Controller"));
}
}
}

In the above class the first line registers all your controllers with lifestyle configured to Transient. Depending on the lifestyle you choose Windsor will clean all resources. Other lifestyles are Singleton, Pooled, PerWebRequest, PerThread. That’s why it is strongly recommended to implement IDisposable on your classes so Windsor can handle all your resources automatically.

You also need to implement a WindsorControllerFactory. This will attach the System.Web.Mvc.DefaultControllerFactory to Windsor.

WindsorControllerFactory.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
using System;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Castle.MicroKernel;

namespace Website.Infrastructure
{
public class WindsorControllerFactory : DefaultControllerFactory
{
private readonly IKernel kernel;

public WindsorControllerFactory(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);
}
}
}

Last but not least you have to bootstrap your container. This is done in your Application_Start() method in the global.asax file.

Global.asax
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Castle.Windsor;
using Castle.Windsor.Installer;
using Website.Infrastructure;

namespace Website
{
// Note: For instructions on enabling IIS6 or IIS7 classic mode,
// visit http://go.microsoft.com/?LinkId=9394801

public class MvcApplication : System.Web.HttpApplication
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}

private void BootstrapContainer()
{
Container = new WindsorContainer()
.Install(FromAssembly.This());

var controllerFactory = new WindsorControllerFactory(Container.Kernel);
ControllerBuilder.Current.SetControllerFactory(controllerFactory);
}

public IWindsorContainer Container { get; private set; }

protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();

RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);

BootstrapContainer();
}
}
}

With this things setup all the dependencies you registered in your WindsorInstaller will be automatically injected to your controllers. Below I have added an example controller implementation to show you how to use the dependencyInjection.

ProductController.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
using System;
using System.Linq;
using System.Web.Mvc;
using Website.Repositories;

namespace Website.Controllers
{
public class ProductController : Controller
{
private readonly IProductRepository _repository;

public ProductController(IProductRepository repository)
{
_repository = repository;
}

public ActionResult Index()
{
var products = _repository.All();
return View(products);
}

public ActionResult Details(int id)
{
var product = _repository.GetById(id);
return View(product);
}
//Other members left for convenience......
}
}

To keep track on your resources you should implement IDisposable on your repositories so resources will be released automatically. In the example I use Entity Framework Code First. Because I implemented the Dispose method, also my connection to my database will be released without the need to think about it everywhere in my code.

ProductRepository.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
using System;
using System.Linq;
using Website.Infrastructure;
using Website.Models;

namespace Website.Repositories
{
public class ProductRepository : IProductRepository, IDisposable
{
private WebsiteReadModelContext _context = new WebsiteReadModelContext();

public IEnumerable<Product> All()
{
_context.Products.Select();
}

public Product GetById(int Id)
{
_context.Products.SingleOrDefault(p => p.Id == id);
}

//Other members left for convenience......

public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (_context != null)
{
_context.Dispose();
_context = null;
}
}
}
}
}

I hope you found this blog post usable.

Share