Loading...
Loading...
Marco Franssen

In this post, I'll explain how to inject dependencies into ASP.NET MVC 3 controllers with Castle Windsor, from configuring the container to using constructor injection.
First, create a WindsorInstaller. This class configures your inversion-of-control (IoC) container.
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 this class, the first registration adds your controllers with a Transient lifestyle. The lifestyle you choose determines how Windsor manages component instances and their lifetimes. Other lifestyles include Singleton, Pooled, PerWebRequest, and PerThread. For classes that own disposable resources, implement IDisposable so Windsor can dispose of them when those components are released.
You'll also need a WindsorControllerFactory. This connects System.Web.Mvc.DefaultControllerFactory to Windsor for controller creation and release.
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);
}
}
}Finally, bootstrap the container in the Application_Start() method in global.asax.
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 setup, Windsor injects the dependencies registered in your WindsorInstaller into your controllers. Below is an example of constructor injection in a controller.
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 manage resource cleanup, implement IDisposable on repositories that own disposable resources. In this example, I use Entity Framework Code First. The Dispose method releases my database context, so I don't have to repeat that cleanup throughout my code.
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 post useful.
Marco Franssen
How the order of FakeItEasy call configurations affected my CQRS domain tests when aggregate roots prepared the event store before an edit.
Marco Franssen
Lessons from applying DDD and CQRS in a Scrum project: bounded contexts, aggregate roots, domain services, guard methods, and command-based tests.
Marco Franssen
My notes from Greg Young's DDD/CQRS course on aggregates, entities, value objects, command-query separation, and consistency through event merging.