Ninject with ASP.NET MVC series

ActionFilters are a great way to encapsulate pieces of functionalities and move them outside ASP.NET MVC Controllers, but they have a problem: it’s very difficult to inject dependencies into them. In this post I want to show you how to use Ninject to inject dependencies into an ActionFilter, first using a simple but not “SOLID”  approach, and then writing some helper classes that allow you not change anything in the way you write your code.

All this post takes for granted that you know how to use Ninject together with ASP.NET MVC. You don’t know, or want to have a refresh, consider reading my previous post on the topic: How to use Ninject with ASP.NET MVC

Why is it difficult to inject dependencies into ActionFilters?

Typically, to inject dependencies into an object, you have to be in control of the instantiation process, either directly or through a Inversion of Control container. But the problem with ActionFilters is that they are .NET attributes, and thus they are instantiated directly by the .NET framework using reflection. This means that you are not in control of the creation process and that you have to inject the needed dependencies later in the process. There a few approaches to solve this problem, and the first we will explore is the easiest but also the worst one.

The scenario

But before digging into the possible solutions let’s see the code of the samples we are going to use in the post. We just want to add to the title of any view a greeting that is retrieved through an external service (the same service used as sample in my previous post about the NinjectControllerFactory for ASP.NET MVC). I’m using the HomeController that is generated by the default ASP.NET MVC Project Template.

using System.Web.Mvc;
using ActionFilterInjection.ActionFilters;

namespace ActionFilterInjection.Controllers
{
[TitleActionFilter] public class HomeController : Controller
{
//Here is your controller implementation } }

Now let’s see the simple but “smelly” way of injecting a dependency inside an ActionFilter.

The static container approach

Since we are not in control of how the action filter is instantiated, the most obvious way of implementing the DI is to create two constructors: one, the default one, used by the runtime and one that is used by you in your unit tests.

public class TitleActionFilterAttribute: ActionFilterAttribute
{
private IGreetingService _service;

public TitleActionFilterAttribute():
this(KernelContainer.Kernel.Get<IGreetingService>()) { }

public TitleActionFilterAttribute(IGreetingService _service)
{
this._service = _service;
}

public override void OnActionExecuted(ActionExecutedContext filterContext)
{
ViewResult result = filterContext.Result as ViewResult;
if (result != null)
{
result.ViewData["Title"] += " - " + _service.GetGreeting();
}
}
}

All the complexity and the differences from the usual ways of writing such classes lie in the empty constructor: it accesses a static KernelContainer that wraps the real IoC container you are using, gets an instance of the service we want to inject, and then it calls the other constructor with the IoC instantiated service.

The problem here is that you are coupling the action filter with your IoC container: this is bad because you cannot change IoC container without changing all your action filters, and also because then you need to instantiate the IoC container also in your tests (and thus need a valid IoC configuration). And finally, this is THE IoC AntiPattern.

The other way to solve the problem is a bit more complex, but once implemented (once in a lifetime) your ActionFilter will be clean and with no problematic dependencies.

Extending ASP.NET MVC

ASP.NET MVC has an high level of extensibility, and we can change the way it executes ActionFilters by extending the ActionInvoker class, which is the one responsible for executing the actions inside a controller.

Step 1 - Override InvokeActionMethodWithFilters method

The first step is overriding the InvokeActionMethodWithFilters method.

public class NinjectActionInvoker: ControllerActionInvoker
{
private readonly IKernel _kernel;

public NinjectActionInvoker(IKernel kernel)
{
_kernel = kernel;
}

protected override ActionExecutedContext InvokeActionMethodWithFilters(
ControllerContext controllerContext,
IList<IActionFilter> filters,
ActionDescriptor actionDescriptor,
IDictionary<string, object> parameters)
{
foreach (IActionFilter actionFilter in filters)
{
_kernel.Inject(actionFilter);
}
return base.InvokeActionMethodWithFilters(
controllerContext, filters, actionDescriptor, parameters);
}
}

What I do here is intercepting the filters before they are executed and then injecting the needed dependencies. The trick is done by calling the Inject method of Ninject: it takes an already instantiated class and inject all the dependencies configured inside the container.

Step 2 - Change the ActionFilter to use Property Injection

Then we have to slightly change the implementation of the ActionFilter because now we cannot use the construction injection any more (the filter has already been instantiated by someone else). Instead we have to use Property Injection. But it’s an easy fix, it just removes code:

public class TitleActionFilterAttribute: ActionFilterAttribute
{
public IGreetingService _service { get; set; }

public override void OnActionExecuted(ActionExecutedContext filterContext)
{
ViewResult result = filterContext.Result as ViewResult;
if (result != null)
{
result.ViewData["Title"] += " - " + _service.GetGreeting();
}
}
}

And the code is also cleaner and shorter.

Step 3 - Set the ActionInvoker in the controllers

As last thing we need to instruct the framework to use our action invoker instead of the default one. This can be done in many different ways:

  • in the default constructor of each controller that needs that customization
  • in a custom controller that you will use as base controller for all your controllers
  • injecting the ActionInvoker inside a custom ControllerFactory

For the first two approaches you would have to write the following line of code in the constructor of your controllers:

this.ActionInvoker = KernelContainer.Kernel.Get<NinjectActionInvoker>();

But this line of code again hides a pitfall: you are using again a static IoC container inside your controller, and this is bad for the same reasons you saw earlier. So, if we want to stick to our dogma (“Do not couple your code with a IoC container”), we need to use a custom controller factory to automatically inject the ActionInvoker. This also has the side benefit of not having to remember to manually set the ActionInvoker in all your controllers.

But since we want to inject dependency in action filters, probably we are also doing the same with controllers, and we already using the NinjectControllerFactory, so we can configure it to inject the action invoker for us, without any other customization.

Step 4 - Configure the Ninject container

The last step is configuring Ninject to handle all these injections (again if you don’t know how to integrate Ninject with ASP.NET MVC, I recommend you go and read my previous article on how to setup an ASP.NET MVC application to use Ninject).

First thing you have to configure the GreetingService:

Bind<IGreetingService>().To<GreetingServiceImpl>();

Then you have to instruct Ninject about how to inject that service inside the ActionFilter. You can do it by just decorating the property that holds the reference.

[Inject]
public IGreetingService _service { get; set; }

Personally I don’t mind using the attribute, even if it slightly couples the filter implementation with Ninject, but I know that someone might not like this, so here is how you can do the same thing, using the fluent API inside the configuration module.

Bind<TitleActionFilterAttribute>().ToSelf()
.InjectPropertiesWhere(p => p.Name == "Service");

The line of code says that whenever someone asks for the TitleActionFilterAttribute class, the container should give him that same class and then inject into it the property named “Service”.

Then we have to specify that every time someone asks for an IActionInvoker, it should receive an instance of the custom action invoker we developed before.

Bind<IActionInvoker>().To<NinjectActionInvoker>().Using<SingletonBehavior>();

I’m using the SingletonBehaviour because the action invoker doesn’t maintain any state, so it’s safe to treat it as singleton, and gain a few milliseconds during the instantiation of the class.

The last configuration needed is the one that injects the custom invoker inside every controller.

Bind<IController>().To<HomeController>()
.Only(When.Context.Variable("controllerName")
.EqualTo("Home"))
.InjectPropertiesWhere(p => p.Name == "ActionInvoker");

If you remember from my post about the AutoControllerModule of Ninject, this is what we used to do before the automatic controller module. But now we want to expand the configuration to specify that, after Ninject created the controller, it also has to inject a dependency in the property named “ActionInvoker”. And so we need to do it again, one per controller. Hopefully Nate will come to the rescue and implement this directly into Ninject.

Wrapping up

In this post we saw that extending the ASP.NET MVC framework is the easiest way to inject dependencies into an action filter. And it requires just 3 steps:

  1. Creating a custom ActionInvoker and overriding its InvokeActionMethodWithFilters method
  2. Specifying the custom ActionInvoker inside the controller
  3. Configuring the IoC container

But it’s all boring and repetitive stuff, so, to help you get the most out of this, I extracted the customizations to an external assembly file and I’m also thinking about downloading the source of Ninject and writing a patch to bring this enhancement inside the Ninject integration pack for ASP.NET MVC.

The goal is that you don’t have to do anything more than just designing your action filters to accept dependencies from outside, and configure that dependencies in the IoC configuration.

But this post turned out a bit longer then I initially thought, so I’ll cover this in another post later this week, so if you haven’t already, consider subscribing to my updates via RSS.

While you wait for the easy path, you can start playing with this sample downloading the sample project that contains all the samples.

kick it on DotNetKicks.com