This blog post is about changing the DefaultControllerFactory to a custom one to allow constructor dependency injection. In this post I use
Structuremap,
CommonServiceLocator and I use a slightly changed version of the
CommonServiceLocator.StructureMapAdatpter.
The DefaultControllerFactory needs a constructor without any input parameters. I want to get rid of dependencies in my code, so I put all my interface dependencies in the constructor and want to use structuremap to handle this for me. I have used this in a semi-large project, but in this blog post I will use an example with only one dependency and only one page.
Lets get started:
I start by creating a new ASP.NET MVC project. I then delete all controllers and views except for my home controller and my index view.
Then I add a Tools folder and a PersonRepository class in my new folder. This only has a default constructor and a get method that returns a Person, a simple object with only a name Property. I also extract an interface from PersonRepository called IPersonRepository.
In my homecontroller I create a constructor that takes a Ipersonrepository as a parameter. This is my HomeController now:
using System.Web.Mvc;
using DIControllerFactoryExample.Tools;
namespace DIControllerFactoryExample.Controllers
{
public class HomeController : Controller
{
private readonly IPersonRepository personRepository;
public HomeController(IPersonRepository personRepository)
{
this.personRepository = personRepository;
}
public ActionResult Index()
{
var personid = 1;
ViewData.Model = personRepository.Get(personid);
return View();
}
}
}
I compile, run and get a: "No parameterless constructor defined for this object." exception. Just as planned.
Now, the fun begins. Getting this to work.
I start by adding a solution folder called Lib, and adding StructureMap.dll and Microsoft.Practices.ServiceLocation.dll and reference them in the solution. These can be found at the locations in the top of this blog post.
Then I add 3 new files to my Tools folder.
StructureMapServiceLocator.cs, DefaultStructureMapRegistry.cs, CommonServiceLocatorControllerFactory.cs
These files look like this:
using StructureMap.Configuration.DSL;
namespace DIControllerFactoryExample.Tools
{
public class DefaultStructureMapRegistry : Registry
{
public DefaultStructureMapRegistry()
{
ForRequestedType<IPersonRepository>().TheDefaultIsConcreteType
<PersonRepository>();
}
}
}using System;
using System.Collections.Generic;
using Microsoft.Practices.ServiceLocation;
using StructureMap;
namespace DIControllerFactoryExample.Tools
{
public class StructureMapServiceLocator : ServiceLocatorImplBase
{
private IContainer container;
public StructureMapServiceLocator(IContainer container)
{
this.container = container;
}
protected override object DoGetInstance(Type serviceType, string key)
{
return string.IsNullOrEmpty(key) ? container.GetInstance(serviceType) : container.GetInstance(serviceType, key);
}
protected override IEnumerable<object> DoGetAllInstances(Type serviceType)
{
foreach (object obj in container.GetAllInstances(serviceType))
{
yield return obj;
}
}
}
}
using System;
using System.Web.Mvc;
using Microsoft.Practices.ServiceLocation;
namespace DIControllerFactoryExample.Tools
{
public class StructureMapServiceLocatorControllerFactory : DefaultControllerFactory
{
protected override IController GetControllerInstance(Type controllerType)
{
var controller = ServiceLocator.Current.GetInstance(controllerType) as Controller;
return controller;
}
}
}
I also add the following to the Application_Start method in my Global.asax.cs:
var registry = new DefaultStructureMapRegistry();
var container = new Container(registry);
ServiceLocator.SetLocatorProvider(() => new StructureMapServiceLocator(container));
var locator = new StructureMapServiceLocatorControllerFactory();
ControllerBuilder.Current.SetControllerFactory(locator);
I compile, reload and everything works. That's is. Good luck;)
EDIT: Did a small fix in the code i global.asax, check out this
postThe full Visual Studio solution can be downloaded here:
DIControllerFactoryExample.zip (463.5 KB)