Friday, May 29, 2009 11:48 PM
Using an IoC is one of those things that can take a time or two to understand where it fits in and how it’s useful. Once you cross the energy barrier, it’s a color in your palette you can’t imagine missing.
Imagine having a QuestionController that takes an interface called DomainRepository. The job of any concrete implementation of the DomainRepository will be to fetch various domain entities from a backing store.
public class QuestionController : Controller
{
private DomainRepository _repository;
/// <summary>
/// Initializes a new instance of the <see cref="QuestionController"/> class.
/// </summary>
/// <param name="repository">The domain repository provides a source for the domain model</param>
public QuestionController(DomainRepository repository)
{
_repository = repository;
}
//
// GET: /Question/List
public ActionResult List()
{
var questions = _repository.GetQuestions();
return View("List", questions);
}
You’d like to use dependency injection to provide the concrete implementation so that you could switch it up for a mock or stub when testing, a Linq to SQL implementation during production, and to make switching to something else an easy task if needed in the future.
The gotcha is that ASP.NET MVC, by default, requires its controllers to have default constructors. Luckily, ASP.NET MVC uses a ControllerBuilder to instantiate the controllers using a ControllerFactory. This is an extension point where we can provider our own factory, one that uses the IoC of choice to resolve the controller.
First create the new ControllerFactory. Here, I’m using StructureMap as my IoC of choice, but this is all it takes.
public class StructureMapControllerFactory : DefaultControllerFactory
{
protected override IController GetControllerInstance(Type controllerType)
{
return ObjectFactory.GetInstance(controllerType) as IController;
}
}
Then in your bootstrapper, or in application start of your Global.asax, simply set the ControllerFactory
ControllerBuilder.Current
.SetControllerFactory(new StructureMapControllerFactory());
An IoC container is a wonderful tool. It will now figure out how to construct the requested controller, and if that controller requires something else, how to make that, and if that something else requires something else, and so on, and so on…
The way concrete types are registered to their interfaces varies based on the container. For structure map, the following entries in the bootstrapper suffice for this example.
ObjectFactory.Initialize(factory =>
{
factory.ForRequestedType<DomainRepository>()
.TheDefaultIsConcreteType<FakeDomainRepository>();
});