MVC was designed with loose coupling and testability in mind which
has made dependency injection reasonably simple since day one. The
first version of MVC already shipped with an interface called IControllerFactory
which was responsible for (you guessed it) locating and creating
controllers. Since the controller is usually the entry point into your
MVC application this is where you would choose to hook into whatever
dependency injection framework you’re using.
MVC 3 makes dependency injection a lot simpler – it’s built into the
framework in a number of ways. Firstly, it exposes an interface called IDependencyResolver
which acts as a wrapper around pretty much any dependency injection
framework. Let’s take a look at how this works in practice.
How is it used
For this example I’m going to use Ninject as a dependency injection framework. I usually prefer StructureMap, but someone on Twitter recommended Ninject so I’m going to try it out.
To work with the new IDependencyResolver interface I would
usually have to write a wrapper around the dependency injection
framework to implement this interface and then register that wrapper
with MVC. However, a wrapper for Ninject is already available on Nuget
so I’m going to use that. Simply type ‘install-package Ninject.MVC3’
in the Package Manager Console (View –> Other Windows –> Package
Manager Console) and it will install both the wrapper and the Ninject
framework for you. Pretty cool.
As an example I’m going to write a simple controller that returns a
list of products – pretty much the same example I used in my last post.
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
public class ProductController : Controller
{
private readonly IProductRepository productRepository = new ProductRepository();
public ActionResult Index()
{
var products = productRepository.GetProducts().ToList();
return View(products);
}
public ActionResult Detail(int id)
{
var selectedProduct = productRepository.GetProducts().Single(p => p.Id == id);
return View(selectedProduct);
}
}
|
The ProductController here has a direct dependency on the ProductRepository class. What I would like to do is register the ProductRepository as an implementation of the IProductRepository interface and then only reference the interface.
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
public class ProductController : Controller
{
private readonly IProductRepository productRepository;
public ProductController(IProductRepository productRepository)
{
this.productRepository = productRepository;
}
public ActionResult Index()
{
var products = productRepository.GetProducts().ToList();
return View(products);
}
public ActionResult Detail(int id)
{
var selectedProduct = productRepository.GetProducts().Single(p => p.Id == id);
return View(selectedProduct);
}
}
|
Now we simply need to register this implementation with our
dependency injection framework. MVC will automatically inject this
dependency when creating our controller.
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
public static void RegisterServiceLocator()
{
IKernel kernel = new StandardKernel();
RegisterServices(kernel);
DependencyResolver.SetResolver(new NinjectServiceLocator(kernel));
}
public static void RegisterServices(IKernel kernel)
{
kernel.Bind<IProductRepository>().To<ProductRepository>();
}
|
If you run this code the ProductRepository will automatically get injected into our ProductController. Cool stuff.
So what exactly did we gain here – all of this was already possible in MVC 2? The only difference is that we need to implement IDependencyResolver to wrap our dependency injection framework instead of implementing our own ControllerFactory that does pretty much the same thing. The difference is that our implementation of IDependencyResolver will now be used for instantiating every custom object we implement.
Dependency Injection for Filters
Dependency Injection has never been available for filters. Keep in
mind that filters are attributes and therefore created by the .NET
runtime – we cannot use a traditional dependency injection strategy.
MVC 3 introduces the concept of filter providers – simply put our
dependency injection framework can inject dependencies, but not through
the constructor.
I’m going to implement a very simple MessageToAction filter that simply writes a message into the rendered view. I would like this attribute to have a dependency on IMessageService – I want Ninject to inject the concrete implementation of this dependency.
|
1
2
3
4
5
6
7
8
9
10
11
|
public class MessageToActionAttribute : ActionFilterAttribute
{
[Inject]
public IMessageService MessageService { get; set; }
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
var actionName = filterContext.ActionDescriptor.ActionName;
filterContext.Controller.ViewBag.FilterMessage = MessageService.GetMessageForAction(actionName);
}
}
|
I need to explicitly tell Ninject to inject this property – as I
mentioned we can’t do this through the constructor since attributes are
created by the .NET runtime. Now we need to do some extra plumbing to
give Ninject a chance to perform the injection once our filters are
created.
We need to register an instance of IFilterProvider – to make life a little easier I’m going to extend the built-in FilterAttributeFilterProvider class.
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
public class NinjectFilterProvider : FilterAttributeFilterProvider
{
private readonly IKernel kernel;
public NinjectFilterProvider(IKernel kernel)
{
this.kernel = kernel;
}
public override IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
var filters = base.GetFilters(controllerContext, actionDescriptor);
foreach (var filter in filters)
{
kernel.Inject(filter.Instance);
}
return filters;
}
}
|
Now we need to register our filter provider with MVC.
|
1
|
FilterProviders.Providers.Add(new NinjectFilterProvider(kernel));
|
The last step is to register the concrete implementation of our IMessageService interface.
|
1
2
3
4
5
|
public static void RegisterServices(IKernel kernel)
{
kernel.Bind<IProductRepository>().To<ProductRepository>();
kernel.Bind<IMessageService>().To<MessageService>();
}
|
And that’s it! Dependency injection for action filters. Pretty cool.
How to do this in MVC 2
You can’t really do this in MVC 2 – in the past I’ve always resorted
to simply having a dependency on the concrete implementation.
Keep in mind that I’ve only illustrated one area where MVC 3
allows dependency injection. With the changes in MVC 3 we can now do
dependency injection in a number of areas – view engines, view pages,
model validators, value providers, model binders – the list goes on.