During the keynote of the
#mvcconf virtual conference,
Scott Hanselman said that only 3 contributions have been made to the open source
Nerd Dinner project. 3?! No wonder my Java friends scoff when I say there is open source in the .Net world. We (.Netters) should be ashamed. Most of us either cut our
MVC teeth on Nerd Dinner, or we at least refined our skills by reviewing its implementation.
I will be conducting a series of experiments in dependency injection for perso
del.icio.us Tags:
.Net,
MVC2 nal reasons, and I will be using Nerd Dinner as my reference MVC application. It’s simple, it’s a known quantity, and perhaps something over the coming weeks will be worthy of contribution to the project. This post will not likely be worthy of contribution; I am offering nothing new. I am adding dependency injection using
StructureMap to an MVC project. This has been done before, but I have to get to step one before I can get to step 2.
Others have demonstrated StructureMap on MVC, and I will be borrowing heavily from Jimmy Bogard of LosTechies.com. Jimmy has forgotten more about dependency injection than I will ever know, and he has an in-depth series on dependency injection in .Net MVC. Jimmy goes deep into MVC to isolate dependencies, and my interest will be in looking a various alternatives to dependency injection applied to a well known reference implementation. I am not a purest. I believe DI and TDD are critical to quality code, I am believe DI frameworks and mocking are better than fakes and “poor man’s DI, and I agree with Jimmy’s position that the ActionResult is a kind of black box with too much responsibility. However, beyond that, I do not (yet) care. I want my code to be good, but I want my code to get to market. An application only has value when it is being used to solve a problem. While I am a developer, I have also run development companies, and I am looking for quality with efficiency.
Nerd Dinner
Nerd Dinner does not use a dependency injection framework, rather it uses a default constructor to enable dependency injection of the form:
IDinnerRepository dinnerRepository;
public DinnersController()
: this(new DinnerRepository()) {
}
public DinnersController(IDinnerRepository repository) {
dinnerRepository = repository;
}
The empty constructor is required by the default MVC controller factory. The second constructor allows dependencies to be injected. In small applications this type of dependency injection works fine. However, in even moderately sized applications, the inherent dependency on the concrete classes (DinnerRepository in this case) becomes a problem. If you decide to use a NewDinnerRepository, you may be facing code changes in many places instead of a single configuration or registry. Let’s apply StructureMap.
Assuming you have installed StructureMap and added a refernce in your project to StructureMap.dll, the first thing you want to do is to tell the application to use StructureMap.
void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
//initialize structuremap container
Bootstrapper.Initialize();
//let structuremap handle creating controllers
ControllerBuilder.Current.SetControllerFactory(new StructureMapControllerFactory());
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new MobileCapableWebFormViewEngine());
}
Step 1 is to initialize StructureMap. A common solution is to use a Bootstrapper class to handle the initialization. Following initialization, we tell the ControllerBuilder to use the StructureMapControllerFactory as its controller factory. Now for the Bootstrapper initialization:
public class Bootstrapper
{
public static void Initialize()
{
ObjectFactory.Initialize(x => x.AddRegistry<NerdModelRegistry>());
}
}
In the initialize method, StructureMap’s ObjectFactory is called to configure the default mapping of Interface objects to concrete objects. For a project of any size, using one or more registries will help keep your configuration organized.
public class NerdModelRegistry : Registry
{
public NerdModelRegistry()
{
//Repositories
For<IDinnerRepository>().Use<DinnerRepository>();
}
}
A registry can be used to map interface definitions to default concrete classes. So now we have configured StructureMap to provide dependency injection for Nerd Dinner, at least the controllers, and we have told the application to use the StructureMapControllerFactory instead of the default ControllerFactory. However, we have not defined a StructureMapControllerFactory. That’s next.
public class StructureMapControllerFactory: DefaultControllerFactory
{
protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
{
return base.GetControllerInstance(requestContext, controllerType);
}
return ObjectFactory.GetInstance(controllerType) as Controller;
}
}
The StructureMapControllerFactory creates a controller and does not require a controller with an empty constructor. This implementation of the StructureMapControllerFactory is fairly common, but Jimmy Bogard would take this a step farther. He recommends injecting the container into the factory, and then use the container to get an instance of the controller. I believe Jimmy is more right than I am, and I encourage you to review his compelling argument. However, I am not yet convinced there is a return on code (R.O.C.) for container injection.
With StructureMap in place and configured, let’s revisit our controller constructor. Effectively, the empty constructor with the default dependency on the concrete repository goes away.
IDinnerRepository dinnerRepository;
//public DinnersController()
// : this(new DinnerRepository()) {
//}
public DinnersController(IDinnerRepository repository) {
dinnerRepository = repository;
}
Pretty trivial change to the controller, huh? However, consider that this same change will be made to the Dinner, RSVP, Search, and Services controllers. Now mentally scale this out to a large real-life production application. If you want to change a repository, a service, a logger, etc., changing references to the concrete types can occur all over your code, or it can happen at a single location in your Registry class. Further, consider what this type of change could mean for regression testing. Dependency injection frameworks make unit testing and TDD much easier as we shall see in coming weeks.
Now there’s one gotcha remaining. I excluded the AccountController from the dependency injection. MVC will use the still use the empty constructor of the Account controller, and I don’t want to invest effort into the AccountController. I expect to experiment with authentication alternatives in the near future. For now I need a band-aid.
[StructureMap.DefaultConstructor]
public AccountController()
: this(null, null) {
}
For now we’ll use the StructureMap DefaultConstructorAttribute. Yes, this is technical debt that will need to be repaid, but I am considering the AccountController as technical debt that needs to be addressed.
This implementation of StructureMap is not new, and DI purists may dismiss its simplicity. However, I argue that this implementation achieves a significant dependency injection benefit with very little code and very little effort. Next we will build upon this sample to use automocking with Moq.
4d92caee-69b1-483a-8269-46a33b342423|0|.0