Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Unity to inject dependencies into a custom ActionFilter

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?

like image 402
Andrew Best Avatar asked Nov 20 '11 05:11

Andrew Best


1 Answers

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;
    }
}

like image 141
Adam Tuliper Avatar answered Oct 26 '22 00:10

Adam Tuliper