Dependency Injection ASP.net MVC 3 Castle Windsor
Unit tests, Test Driven Development, Behavior Driven Development, Inversion of Control, and Dependency Injection…. Whew! Don’t I sound smart!? These big dev words are concepts that many modern applications are using. TDD and BDD in particular are some great design patterns to help create “bug free code”.
Dependency Injection and Inversion of Control (DI and IoC) are two terms that mean relatively the same thing, and they are very useful when doing TDD, BDD, and just regular old unit tests. One of the things that can make testing complicated is when one thing depends on another thing.
public class HomeController : Controller
{
//
// GET: /Home/
private readonly IMyRepo _repo;
public HomeController(IMyRepo repo)
:this(new MyRepo()){
}
public HomeController(IMyRepo repo)
{
_repo = repo;
}
public ActionResult Index()
{
var stuff = _repo.GetStuff();
return View(stuff);
}
}
The above code is known as “poor man’s dependency injection”. The basic point there being, when the default constructor of HomeController is called it shoves in a MyRepo object. This could be problematic during testing because if you need to mock or stub that object, you’d have to mess around with some stuff.
An easier and safer way to promote separation of concerns, another big word, is to use an actual DI framework such as Castle.Windor or Ninject .
I have used both, but this post will cover some Castle.Windsor stuff.
In order for Castle.Windsor to work, you will need to create a custom controller factory in some directory under your main project like Plumbing.
public class WindsorControllerFactory : DefaultControllerFactory
{
private readonly IKernel _kernel;
/// <summary>
/// Constructor
/// </summary>
/// <param name="kernel"></param>
public WindsorControllerFactory(IKernel kernel)
{
_kernel = kernel;
}
/// <summary>
/// Release the controller at the end of it's life cycle
/// </summary>
/// <param name="controller">The Interface to an MVC controller</param>
public override void ReleaseController(IController controller)
{
_kernel.ReleaseComponent(controller);
}
/// <summary>
/// Resolve a controller dependency
/// </summary>
/// <param name="requestContext">The HTTP context</param>
/// <param name="controllerType">Type of controller to resolve</param>
/// <returns>IController</returns>
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 will create instances of your controllers for you and throw a 404 error when the controller you are looking for does not exist.
Next up is the Installer for the controllers. Installers are how you tell Castle what to resolve when asked for some object. They inherit from IWindsorInstaller
public class ControllersInstaller : IWindsorInstaller
{
/// <summary>
/// Installs the controllers
/// </summary>
/// <param name="container"></param>
/// <param name="store"></param>
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(FindControllers().Configure(ConfigureControllers()));
}
/// <summary>
/// Find controllers within this assembly in the same namespace as HomeController
/// </summary>
/// <returns></returns>
private BasedOnDescriptor FindControllers()
{
return AllTypes.FromThisAssembly()
.BasedOn<IController>()
.If(Component.IsInSameNamespaceAs<HomeController>())
.If(t => t.Name.EndsWith("Controller"));
}
/// <summary>
/// Returns the transient lifestyle for the controllers.
/// </summary>
/// <returns></returns>
private ConfigureDelegate ConfigureControllers()
{
return c => c.LifeStyle.Transient;
}
}
Then the Installer for the repository.
public class RepoInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(AllTypes.FromAssemblyNamed("YourAssembly")
.Where(type => type.Name.EndsWith("Repo"))
.WithService.DefaultInterface()
.Configure(c => c.LifeStyle.Singleton));
}
}
And finally the code that ties it all together in Global.asax under Application_Start()
private static void BootstrapContainer()
{
var container = new WindsorContainer(); // Create a container to hold the dependencies
var controllerFactory = new WindsorControllerFactory(container.Kernel); // Create a new instance
ControllerBuilder.Current.SetControllerFactory(controllerFactory); // Use my factory instead of default
container.Install(
FromAssembly.This()
); // Scan this assembly for all IWindsorInstaller
}
So, basically what we have done here is, now your controller code looks like this…
public class HomeController : Controller
{
//
// GET: /Home/
private readonly IMyRepo _repo;
public HomeController(IMyRepo)
{
_repo = repo;
}
public ActionResult Index()
{
var stuff= _repo.GetStuff();
return View(stuff);
}
}
So, during the execution of your code when your HomeController get’s spun up, it will ask Castle.Windsor for an IMyRepo object. Then based on the RepoInstaller, it will find an object that implements RepoInstaller and automatically shove in a new MyRepo object.
Magical isn’t it!
4 Responses to Dependency Injection ASP.net MVC 3 Castle Windsor
Leave a Reply Cancel reply
Categories
- ASP.NET (10)
- Backbonejs (2)
- C# (3)
- CoffeeScript (1)
- Coldfusion (4)
- Entity Framework (2)
- Git (1)
- Google (1)
- JavaScript (3)
- jQuery (4)
- KnockoutJS (1)
- Linq (1)
- MVC 3 (6)
- MVC4 (1)
- Source Control (1)
- Uncategorized (4)
- Web API (1)
- Webmatrix (2)
- Welcome (1)
Twitter: jcreamer898
- @popcrackerpop @yahoo I'm in... 04:41:20 PM May 18, 2012 from web in reply to popcrackerpop ReplyRetweetFavorite
- All I can think about are expression trees, tasks, generics, and dynamics what a great @nashdotnet @bryan_hunter @digitalBush @reverentgeek 04:11:36 AM May 18, 2012 from Twitter for iPhone ReplyRetweetFavorite
- RT @ChrisLove: jQuery/JavaScript Tools and Plugins Worth Checking Out http://t.co/JhocAhYo #webdev #javaScript #ajax #jquery 09:00:31 PM May 17, 2012 from TweetDeck ReplyRetweetFavorite
- @smashingmag Inbox count < 10 08:14:31 PM May 17, 2012 from TweetDeck in reply to smashingmag ReplyRetweetFavorite
- @bryan_hunter @reverentgeek I will be there a little early if you want to try and whip something up on my laptop? 07:37:53 PM May 17, 2012 from TweetDeck in reply to bryan_hunter ReplyRetweetFavorite
- @ifandelse That sounds fun! 07:10:40 PM May 17, 2012 from TweetDeck in reply to ifandelse ReplyRetweetFavorite
- @kunal2383 You bet! I downloaded it and it's pretty awesome stuff... 07:02:30 PM May 17, 2012 from web in reply to kunal2383 ReplyRetweetFavorite
- RT @kunal2383: Introducing “dotPeek” - A free .NET Decompiler by JetBrains http://t.co/izw1bJC0 06:56:52 PM May 17, 2012 from TweetDeck ReplyRetweetFavorite
- RT @rogeriopvl: Nodo: command line TODO app in Node http://t.co/WaEulXXa #nodejs #nodo 06:47:00 PM May 17, 2012 from TweetDeck ReplyRetweetFavorite
- @csell5 Famous last words... :) 04:24:53 PM May 17, 2012 from TweetDeck in reply to csell5 ReplyRetweetFavorite
Coldfusion
Development Sites
Archives
- April 2012 (1)
- January 2012 (1)
- December 2011 (1)
- November 2011 (2)
- October 2011 (1)
- August 2011 (1)
- July 2011 (2)
- June 2011 (3)
- May 2011 (3)
- April 2011 (1)
- March 2011 (1)
- February 2011 (2)
- January 2011 (4)






Jonathan,
Thank you very much for providing such clear and to the point post. Most of what we see on the internet doesn’t explain in this clear manner.
I was able to implement DI with Castle Windsor in my project using your guidance and it’s working great!
All the best and keep up these great posts!
Leniel
What makes you choose between where() and if()
- In one you have .If(t => t.Name.EndsWith(“Controller”));
- In another you have .Where(type => type.Name.EndsWith(“Repo”))
I only ask because I was googling as to why I’m getting different results depending which of these 2 methods and which order I put them in.
ConfigureDelegate is an unknown class.. are there using statements that you aren’t showing that we need? It can’t resolve itself. I’m using Windsor 3
You can use next code:
container.Register(FindControllers().Configure(c => c.LifestyleTransient()));