Using Windsor to inject dependencies into ASP.NET MVC ActionFilters

I'm using Windsor as my IoC container for an ASP.NET MVC application.  To get dependency injection in my controllers, I'm using a slightly modified WindsorControllerFactory from Andre Loker's post earlier this year.  It works great and allows me to easily test my controllers.

I've got some custom ActionFilters that would benefit from dependency injection.  Unfortunately, ActionFilters are attributes on controllers and methods and their instantiation is controlled by the framework.  There is no extension point to allow custom creation of the ActionFilters.  So I can't do constructor dependency injection.  However, I can do the next best thing – property dependency injection!

Custom Method Invoker

The ASP.NET MVC framework has an extensibility point when it comes to actually invoking actions on controllers.  The default ControllerActionInvoker does everything we need.  We need to modify it's behavior just a little bit to allow us to inject property dependencies.

There's a method on ControllerActionInvoker that is used whenever an action with filters is going to be executed.  It's appropriately named "InvokeActionMethodWithFilters".  This method is passed a collection of ActionFilters that have already been created by the framework (see, this is why we can't use constructor injection).  Thanks to Simone Chiaretta for blogging about his custom Ninject-based ActionInvoker, I was able to convert his to a Windsor-based invoker with relative ease:

public class WindsorActionInvoker : ControllerActionInvoker
{
    readonly IWindsorContainer container;
 
    public WindsorActionInvoker(IWindsorContainer container)
    {
        this.container = container;
    }
 
    protected override ActionExecutedContext InvokeActionMethodWithFilters(
            ControllerContext controllerContext,
            IList<IActionFilter> filters,
            ActionDescriptor actionDescriptor,
            IDictionary<string, object> parameters)
    {
        foreach (IActionFilter actionFilter in filters)
        {
            container.Kernel.InjectProperties(actionFilter);
        }
        return base.InvokeActionMethodWithFilters(controllerContext, filters, actionDescriptor, parameters);
    }
}

As you can see, it's pretty straightforward.  All we do is loop through all of the ActionFilters and inject any dependant properties.  Those of you familiar with Windsor will realize that the IKernel doesn't have an "InjectProperties" method.  I grabbed that from Jeremy Skinner's post about using AutoFac to inject properties into ActionFilters.  He created an extension method that uses reflection to resolve property dependencies:

public static class WindsorExtension
{
    public static void InjectProperties(this IKernel kernel, object target)
    {
        var type = target.GetType();
        foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
        {
            if (property.CanWrite && kernel.HasComponent(property.PropertyType))
            {
                var value = kernel.Resolve(property.PropertyType);
                try
                {
                    property.SetValue(target, value, null);
                }
                catch (Exception ex)
                {
                    var message = string.Format("Error setting property {0} on type {1}, See inner exception for more information.", property.Name, type.FullName);
                    throw new ComponentActivatorException(message, ex);
                }
            }
        }
    }
}

Now that we have the plumbing, let's hook it up!

Changes to WindsorControllerFactory

My current implementation of WindsorControllerFactor.GetControllerInstance looks like this:

protected override IController GetControllerInstance(Type controllerType)
{
    if( controllerType == null)
    {
        return base.GetControllerInstance(controllerType);
    }
    var controller = container.Resolve(controllerType) as Controller;
 
    return controller;
}

I just need to add a few lines of code to add my custom WindsorActionInvoker (which is registered inside Windsor!):

protected override IController GetControllerInstance(Type controllerType)
{
    if( controllerType == null)
    {
        return base.GetControllerInstance(controllerType);
    }
    var controller = container.Resolve(controllerType) as Controller;
 
    // new code
    if (controller != null)
    {
        controller.ActionInvoker = container.Resolve<IActionInvoker>();
    }
 
    return controller;
}

Using Property Injection with ActionFilters

So now lets look at how this can be used to add common data to every page.  The oft-used example of populating a list of sponsors just happened to be the exact scenario I was facing when I researched this solution.  With the WindsorActionInvoker in place, I can now create an ActionFilter to load my sponsor information into ViewData:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class LoadSponsorsAttribute : ActionFilterAttribute
{
    public ISponsorRepository SponsorRepository { get; set; }
 
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        filterContext.Controller.ViewData["sponsors"] = SponsorRepository.GetSponsors();
        base.OnActionExecuting(filterContext);
    }
}

Now any method (or an entire controller class) can have this attribute applied and the sponsor information will be loaded before the action is called.  Here's an example:

[LoadSponsors]
public class ErrorController : ControllerBase
{
    public ActionResult Index()
    {
        return View();
    }
}

The list of sponsors is available in your view pages and user controls by simply looking at the ViewData dictionary:

var sponsors = (IEnumerable<Sponsor>) ViewData["sponsors"];

Final Polish

A nice solution, but the use of magic string constants ("sponsors") along with where the data are stored (ViewData) didn't sit well.  To fix that, I added a few extension methods to abstract out the access to the list of sponsors:

public static class Extensions
{
    private static class ViewDataKeys
    {
        public const string Sponsors = "sponsors";
    }
 
    public static void SetSponsors(this ControllerBase controller, IEnumerable<Sponsor> sponsors)
    {
        controller.ViewData[ViewDataKeys.Sponsors] = sponsors;
    }
 
    public static IEnumerable<Sponsor> GetSponsors(this ViewUserControl viewControl)
    {
        return viewControl.ViewData[ViewDataKeys.Sponsors] as IEnumerable<Sponsor>;
    }
}

The SetSponsors extension method allows me to change my LoadSponsorsAttribute to be simply:

public override void OnActionExecuting(ActionExecutingContext filterContext)
{
    filterContext.Controller.SetSponsors(SponsorRepository.GetSponsors());
    base.OnActionExecuting(filterContext);
}

The GetSponsors extension method on ViewUserControl allows the Sponsors.ascx that I used to display the list to simply call the GetSponsors() method – no access to ViewData or casting required:

var sponsors = this.GetSponsors();

Thanks to Andre Loker, Simone Chiaretta and Jeremy Skinner for making great posts that allowed me to piece all of this together!

2 Comments

  • Nice solution. Btw, for the keys it's better to use "struct" I suppose.

  • As an addendum - I had issues when trying to use the solution above this with filters inherited from AuthorizeAttribute.

    Turns out that in WindsorActionInvoker, I had to override InvokeAuthorizationFilters rather than just InvokeFilters.

    Just a heads up for anyone who might have the same issue.

Comments have been disabled for this content.