OdeToCode IC Logo

Injectable, Configurable Action Filters

Thursday, January 20, 2011

One of the advantages to using a custom IFilterProvider is how you gain total control over the instantiation of action filters in ASP.NET MVC 3. When you combine an IoC container with a filter provider and a custom IDependencyResolver (see Brad's posts for details),  then all the pieces fall into place. Building on the code from the last post, we can change our filter provider to utilize Ninject for action filter instantiation.

public class ConfiguredFilterProvider : IFilterProvider
{
    private readonly IKernel _kernel;

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

    public IEnumerable<Filter> GetFilters(
        ControllerContext controllerContext, 
        ActionDescriptor actionDescriptor)
    {
        var filters = FiltersSettings.Settings.Filters;
        foreach (var filter in filters.Cast<FilterAction>())
        {
            var filterType = Type.GetType(filter.Type);
            yield return new Filter(
                    _kernel.Get(filterType),
                    FilterScope.Global, order:null          
                );
        }
    }
}

Using Ninject to create the filters makes it trivially easy to inject dependencies into the filter. In case you didn't know, action filters in MVC 3 no longer need to derive from an attribute derived base class. All you need is a class that implements one or more of the action filter interfaces.

public class LoggingFilter : IActionFilter, 
                             IExceptionFilter, 
                             IResultFilter, 
                             IAuthorizationFilter
{

    public LoggingFilter(ISomeDependency dependency)
    {
            
    }

    // ...
}

If you are using Ninject, you'll also want to use a custom IDependencyResolver based on Ninject. It's easy to write your own, or you can add one already implemented for you from the NuGet gallery.

image

With the custom dependency resolver in place, you no longer need to register your filter provider in the FilterProvider.Providers collection. Instead, you can just register the provider as another service in the container.

public static class AppStart_NinjectMVC3
{
    public static void RegisterServices(IKernel kernel)
    {
        kernel.Bind<IFilterProvider>()
              .To<ConfiguredFilterProvider>();
        kernel.Bind<ISomeDependency>()
              .To<SomeDependency>();
    }

    public static void Start()
    {
        IKernel kernel = new StandardKernel();
        RegisterServices(kernel);
        DependencyResolver.SetResolver(
            new NinjectServiceLocator(kernel));
    }
}

In previous versions of MVC, filters were notoriously difficult for injection, and mostly inflexible. MVC 3 is a great improvement.