Using Unity to inject dependencies into a custom ActionFilter

10,859

I would do this slightly differently. I would:

  1. install the unity.mvc3 nuget package

  2. call the bootstrapped.initialise() as mentioned in the txt doc the package adds to the project

  3. define your IMetadataService mapping in the initialize to your Concrete type

  4. add IMetadataService to your constructor

The difference between your implementation and the article you references is you use Func, which Im not sure if that adds a different problem to the mix here. I have to imagine it does as the above method (without Func) works fine for me.

Edit: Brad Wilson's code worked just fine for me here: http://bradwilson.typepad.com/blog/2010/07/service-location-pt4-filters.html

Applicable parts (please see the link above)

Global.asax.cs


protected void Application_Start() {
    // ...

    var oldProvider = FilterProviders.Providers.Single(
        f => f is FilterAttributeFilterProvider
    );
    FilterProviders.Providers.Remove(oldProvider);

    var container = new UnityContainer();
    var provider = new UnityFilterAttributeFilterProvider(container);
    FilterProviders.Providers.Add(provider);

    // ...
}

The filter itself:


using System;
using System.Web.Mvc;
using Microsoft.Practices.Unity;

public class InjectedFilterAttribute : ActionFilterAttribute {

    [Dependency]
    public IMathService MathService { get; set; }

    public override void OnResultExecuted(ResultExecutedContext filterContext) {
        filterContext.HttpContext.Response.Write(
            String.Format("

The filter says 2 + 3 is {0}.

", MathService.Add(2, 3)) ); } }

and UnityFilterAttributeFilterProvider.cs


using System.Collections.Generic;
using System.Web.Mvc;
using Microsoft.Practices.Unity;

public class UnityFilterAttributeFilterProvider : FilterAttributeFilterProvider {
    private IUnityContainer _container;

    public UnityFilterAttributeFilterProvider(IUnityContainer container) {
        _container = container;
    }

    protected override IEnumerable GetControllerAttributes(
                ControllerContext controllerContext,
                ActionDescriptor actionDescriptor) {

        var attributes = base.GetControllerAttributes(controllerContext,
                                                      actionDescriptor);
        foreach (var attribute in attributes) {
            _container.BuildUp(attribute.GetType(), attribute);
        }

        return attributes;
    }

    protected override IEnumerable GetActionAttributes(
                ControllerContext controllerContext,
                ActionDescriptor actionDescriptor) {

        var attributes = base.GetActionAttributes(controllerContext,
                                                  actionDescriptor);
        foreach (var attribute in attributes) {
            _container.BuildUp(attribute.GetType(), attribute);
        }

        return attributes;
    }
}

Share:
10,859
Andrew Best
Author by

Andrew Best

Updated on June 09, 2022

Comments

  • Andrew Best
    Andrew Best almost 2 years

    At the moment, I have a custom ControllerFactory into which I inject my Unity container:

    in global.asax Application_Start():

    var container = InitContainer();
    DependencyResolver.SetResolver(new UnityDependencyResolver(container));
    
    var factory = new UnityControllerFactory(container);
    ControllerBuilder.Current.SetControllerFactory(factory);
    

    In the controller factory I set my controllers to use a custom ActionInvoker like so:

    protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
    {
        var controller = base.GetControllerInstance(requestContext, controllerType) as Controller;
    
        if (controller != null)
            controller.ActionInvoker = new UnityActionInvoker(_container);
    
        return controller;
    }
    

    Finally in my custom ActionInvoker, I attempt to buildup actions being invoked using the ActionInvokers container:

    protected override ActionExecutedContext InvokeActionMethodWithFilters(
            ControllerContext controllerContext,
            IList<IActionFilter> filters,
            ActionDescriptor actionDescriptor,
            IDictionary<string, object> parameters)
    {
        var builtUpFilters = new List<IActionFilter>();
    
        foreach (IActionFilter actionFilter in filters)
        {
            builtUpFilters.Add(_container.BuildUp<IActionFilter>(actionFilter));
        }
    
        return base.InvokeActionMethodWithFilters(controllerContext, builtUpFilters, actionDescriptor, parameters);
    }
    

    Here is an example of one of the ActionFilters that is being built up:

    public class PopulatRolesAttribute : ActionFilterAttribute, IActionFilter
    {
        private const string RolesKey = "roles";
    
        [Dependency]
        public Func<IMetadataService> Service { get; set; }
    
        public PopulatRolesAttribute()
        {
        }
    
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            if (filterContext.Controller.ViewData[RolesKey] == null)
            {
                filterContext.Controller.ViewData[RolesKey] = Service().GetRoles();
            }
        }
    }
    

    The problem is that the public property on my custom ActionFilterAttribute is never injected with anything, it remains null on execution! I cannot see why my filter is not being correctly builtup by the container. The type being injected is registered properly, like so:

    container.RegisterInstance(new ChannelFactory<IMetadataService>(
        new BasicHttpBinding(),
        new EndpointAddress("http://example.com/ABSApplication/MetadataService.svc")));
    
    container.RegisterInstance<Func<IMetadataService>>(
        () => container.Resolve<ChannelFactory<IMetadataService>>().CreateChannel());
    

    And is also being injected elsewhere in the application (Although not via .Buildup). This is pretty much the same process followed by this blog post. What piece of the puzzle am I missing?

  • Andrew Best
    Andrew Best over 12 years
    I appreciate the direction to the Unity.Mvc3 package - it encapsulates dependency resolution for Mvc3 using Unity nicely. However I do not think it solves my issue. If I have a method in a controller decorated with a filter like so: [PopulatRoles] public ActionResult Add() { how do I achieve injection upon the PopulateRoles ActionFilterAttribute? I cannot use constructor injection as it is an attribute and I cannot utilize its constructor. Therefore I need to build up the attribute as it is created.
  • Andrew Best
    Andrew Best over 12 years
    As an addendum, I have seen that ninject does something completely different like this link to allow constructor injection on filters, however I am not sure if this is achievable with Unity.
  • Adam Tuliper
    Adam Tuliper over 12 years
    Filters are resolved internally in MVC I'll have to try a quick test here.
  • Andrew Best
    Andrew Best over 12 years
    If I add a constructor to my ActionFilterAttribute like so public PopulateOfficesAttribute(Func<IMetadataService> service) to achieve constructor injection, then I would need to supply that arg when using the attribute in code '[PopulateOffices(Arg)]', otherwise it will not compile. If I try use property injection, the MVC framework does not resolve the dependency within the ActionFilterAttribute for me - just tested it to make sure :) If you find it to be otherwise, let me know!
  • Adam Tuliper
    Adam Tuliper over 12 years
    actually this should work here - going to try it bradwilson.typepad.com/blog/2010/07/…
  • Norman H
    Norman H over 11 years
    @AdamTuliper thanks for the pointer, you saved me some serious time! It looks like the code snippet here has been updated on the link you referenced to match changes to ASP NET that occurred later.
  • Stephen Collins
    Stephen Collins over 9 years
    Quite late to the party, but the Unity Bootstrapper for ASP.NET MVC package does all this work for you.