Monday, July 11, 2011

Injecting Controller Dependencies in .NET MVC with StructureMap

When you are creating an MVC application, your Controllers will by default be created without any dependencies. As soon as you try to inject a dependency into your Controller's constructor...

public EbookController(IEbookRepository ebookRepository)
{
    this.ebookRepository = ebookRepository;
}

You will get an error message something like this:

No parameterless constructor defined for this object.

This is because the MVC framework internally uses a factory to generate Controller objects based on your classes. When the factory tries to instantiate your class, it doesn't have an implementation that handles your dependency.

You can override the default factory with one that uses your preferred Dependency Injection solution (in this example I have used StructureMap), and therefore will take over the controller instantiation and resolve any dependencies on your behalf. You need to do three things:

  1. Bootstrap your dependencies
  2. Override the default Controller factory
  3. Tell your application to use your Bootstrapper and overriden factory

I'm going to demonstrate using the example I've already started above: an Ebook controller depending on an EbookRepository.

1. Bootstrap your dependencies

using StructureMap;

public static class Bootstrapper
{
    public static void Bootstrap()
    {
        ObjectFactory.Initialize(
            x => x.For<IEbookRepository>()
                .Use<EbookRepository>());
    }
}

2. Create a new class to override the default Controller factory with one that uses StructureMap to instantiate Controllers:

public class StructureMapControllerFactory
    : DefaultControllerFactory
{
    protected override IController GetControllerInstance(
        RequestContext requestContext, Type controllerType)
    {
        try
        {
            if (controllerType == null)
                return base.GetControllerInstance(
                    requestContext, controllerType);

            return ObjectFactory.GetInstance(controllerType)
                as Controller;
        }
        catch (StructureMapException)
        {
            System.Diagnostics.Debug.WriteLine(
                ObjectFactory.WhatDoIHave());
            throw;
        }
    }
}

3. Tell your application to use the Bootstrapper and overriden factory, by editing Global.asax.cs:

protected void Application_Start()
{
    //Any other commands
    RegisterRoutes(RouteTable.Routes);

    Bootstrapper.Bootstrap();
    ControllerBuilder.Current.SetControllerFactory(
        new StructureMapControllerFactory());
}

That's it!

The source of the technique used in this article can be found here on Shiju Varghese's blog.

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.