First time here? You are looking at the most recent posts. You may also want to check out older archives or the tag cloud. Please leave a comment, ask a question and consider subscribing to the latest posts via RSS. Thank you for visiting! (hide this)

UPDATE: There is a new version of Ninject.FilterInjector out, version 1.5. You can read more about it on: Ninject.FilterInjector v1.5: now injecting inside all filters 

In the previous posts of the series I explained how to use the Ninject IoC container with ASP.NET MVC and how to adapt the ASP.NET MVC framework to allow injecting dependencies into Action Filters. In this last post of the series I’m going to show you the changes I’ve done to Ninject to make it transparent for the developer to inject dependencies into Action Filters.

What we want to achieve

I want the initialization code of my container to specify the bindings only for the classes of my model and for my services. It doesn’t have to contain infrastructural code.

I want it to be like this:

public override void Load()
{
    Bind<IGreetingService>().To<GreetingServiceImpl>();
}

The code in my previous sample, instead, contained also all the glue to make the custom ActionInvoker method work.

Isolating the changes in an external and reusable library

To make that possible I created an external assembly, and I moved here the custom action invoker that, before invoking the actions, inject the dependencies into the filters.

namespace Codeclimber.Ninject.FilterInjector
{
    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);
        }
    }
}

The other piece of code that is needed for injecting into filter is the one that configures each controller to use the aforementioned ActionInvoker. If you recall from my previous post, it was pretty repetitive, boring and potentially long in case of many controllers. So I modified the AutoControllerModule that comes with Ninject 1.5 to have it inject the custom ActionInvoker as well, and I named it AutoControllerModuleWithFilters.

The only method I changed is the one that loops through all the types of the assembly and register its bindings.

private void RegisterControllers(Assembly assembly)
{
    Bind<IActionInvoker>().To<NinjectActionInvoker>().Using<SingletonBehavior>();
    foreach (Type type in assembly.GetExportedTypes())
    {
        if (!type.IsPublic || type.IsAbstract || type.IsInterface || type.IsValueType)
            continue;

        if (!typeof(IController).IsAssignableFrom(type))
            continue;

        string name = GetNameForController(type);

        Bind(typeof (IController)).To(type).OnlyIf(ctx => CheckControllerName(ctx, name))
            .InjectPropertiesWhere(p => p.Name == "ActionInvoker");
    }
}

The rest of the class is the same as the original one.

How to use the FilterInjector library

I packaged the two classes above in their own assembly, so all you have to do to inject dependencies into ActionFilter is:

  1. Download the Codeclimber.Ninject.FilterInject library (bin and source)
  2. Create a reference to the Codeclimber.Ninject.FilterInject.dll
  3. Configure Ninject using the AutoControllerModuleWithFilters class instead of the AutoControllerModule (after importing the Codeclimber.Ninject.FilterInject namespace)

With this in place you can configure all your dependencies as you always did, and don’t care about whether the dependency needs to be injected into a controller or an action filter. The following code is part of the Global.asax.cs file:

...
using Codeclimber.Ninject.FilterInjector;
...

public class MvcApplication : NinjectHttpApplication
{
    protected override void RegisterRoutes(RouteCollection routes)
    {
        //Your routes here
    }

    protected override IKernel CreateKernel()
    {
        var modules = new IModule[]
             {
                 new AutoControllerModuleWithFilters(Assembly.GetExecutingAssembly()),
                 new ServiceModule(),
             };
        return new StandardKernel(modules);
    }
}

internal class ServiceModule : StandardModule
{
    public override void Load()
    {
        Bind<IGreetingService>().To<GreetingServiceImpl>();
    }
}

As you notice, this code is exactly the same of the one used to configure an ASP.NET MVC application to use Ninject.

Where to get the library

I sent the patch to Nate Kohari, and I hope he is going to include this feature into the official release of Ninject, but in case he doesn’t, I published this small piece of code on my SVN repository on Google. You can download the binary distribution, the source distribution, the sample application, browse the source code online, and if you find bugs or have suggestions, please post them on the online tracking tool.

UPDATE: There is a new version of Ninject.FilterInjector out, version 1.5. You can read more about it on: Ninject.FilterInjector v1.5: now injecting inside all filters 



kick it on DotNetKicks.com

posted on Sunday, February 15, 2009 10:15 PM

Comments on this entry:

# re: Extending Ninject to inject dependencies into Action Filters

Left by Owen Evans at 2/19/2009 2:09 AM

Hi Simone,
I've emailed you a couple of patch files so that this can work with AuthorizationFilters and ResultFilters too.

Great work by the way.

# re: Extending Ninject to inject dependencies into Action Filters

Left by Simone at 2/19/2009 11:05 PM

Thank you about that, I completely forgot to add them when I published the lib as full project.
I'll include and post an update ASAP

Comments have been closed on this topic.