ASP.NET Web API - Inject Dependencies Into ActionFilterAttribute

Jun 12, 2013 at 10:14 AM
Edited Jun 12, 2013 at 10:28 AM
I have Unity running great for all the controllers in my ASP.NET Web API project - just using the default set up that comes out of the NuGet box.

How to I extend this default implementation to inject a dependency into an ActionFilterAttribute, for example...
    public class BasicAuthenticationAttribute : ActionFilterAttribute
    {
        [Dependency]
        public IApiUserService apiUserService { get; set; }

        public BasicAuthenticationAttribute()
        {
        }
    }
This filter is applied to controllers using attributes:
[BasicAuthentication]
I'm pretty sure I need to hook up the Unity container so it handles the creation of the attribute class, but need some clues about where to start!
Jun 14, 2013 at 5:26 AM
Edited Jun 14, 2013 at 9:03 AM
Action Filters are built in to the Unity bootstrapper for ASP.NET MVC so you actually shouldn't have to do anything.

When you add the bootstrapper project via NuGet it will create 2 classes in the App_Start folder: UnityConfig and UnityMvcActivator. UnityMvcActivator will set up the FilterProvider and DependencyResolver. UnityConfig provides a RegisterTypes() method where you can register your types. So all you should have to do is add code to register the IApiUserService:
        /// <summary>Registers the type mappings with the Unity container.</summary>
        /// <param name="container">The unity container to configure.</param>
        /// <remarks>There is no need to register concrete types such as controllers or API controllers (unless you want to 
        /// change the defaults), as Unity allows resolving a concrete type even if it was not previously registered.</remarks>
        public static void RegisterTypes(IUnityContainer container)
        {
            // NOTE: To load from web.config uncomment the line below. Make sure to add a Microsoft.Practices.Unity.Configuration to the using statements.
            // container.LoadConfiguration();

            // TODO: Register your types here
            container.RegisterType<IApiUserService, ApiUserService>();
        }
Once you do that the filter should have the property injected.

~~
Randy Levy
entlib.support@live.com
Enterprise Library support engineer
Support How-to
Jun 14, 2013 at 9:39 AM
Edited Jun 14, 2013 at 9:48 AM
All of the types are registered correctly - they are used by many of the controllers and that all works. It doesn't work for the filter attributes though.

As a potentially interesting side note, Unity added Bootstrapper.cs to the root folder, but not UnityConfig or UnityMvcActivator in the app_start. Maybe this is because I installed the Web API Nuget package?
    public static class Bootstrapper
    {
        public static void Initialise()
        {
            var container = BuildUnityContainer();

            GlobalConfiguration.Configuration.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container);
        }

        private static IUnityContainer BuildUnityContainer()
        {
            var container = new UnityContainer();

            container.RegisterType<IApiUserService, ApiUserService>();
            // Others...

            return container;
        }
    }
Jun 14, 2013 at 6:53 PM
Edited Aug 1, 2014 at 4:56 AM
Sorry, my post was about ASP.NET MVC and not ASP.NET Web API. Oops, my bad.

What version of ASP.NET MVC are you using? I'm using version 4.

Also, based on your code sample it looks like you may be using another NuGet package? Perhaps Unity.Mvc4 or Unity.MVC3?

When I install the official Unity bootstrapper for ASP.NET Web API two files are added to App_Start: UnityConfig.cs and UnityWebApiActivator.cs.

To get injection working for an instance of System.Web.Http.Filters.ActionFilterAttribute I had to create a custom IFilterProvider. To do this I extended ActionFilterDescriptorFilterProvider:
    public class UnityActionFilterProvider : ActionDescriptorFilterProvider, IFilterProvider
    {
        private readonly IUnityContainer container;

        public UnityActionFilterProvider(IUnityContainer container)
        {
            this.container = container;
        }

        public new IEnumerable<FilterInfo> GetFilters(HttpConfiguration configuration, HttpActionDescriptor actionDescriptor)
        {
            var filters = base.GetFilters(configuration, actionDescriptor);

            foreach (var filter in filters)
            {
                container.BuildUp(filter.Instance.GetType(), filter.Instance);
            }

            return filters;
        }
    }

Next I created a helper method, RegisterFilterProviders, to register the provider:
    public static void RegisterFilterProviders()
    {
        var providers = GlobalConfiguration.Configuration.Services.GetFilterProviders().ToList();
        GlobalConfiguration.Configuration.Services.Add(typeof(System.Web.Http.Filters.IFilterProvider),
                                                        new UnityActionFilterProvider(UnityConfig.GetConfiguredContainer()));

        var defaultprovider = providers.First(p => p is ActionDescriptorFilterProvider);
        GlobalConfiguration.Configuration.Services.Remove(typeof(System.Web.Http.Filters.IFilterProvider), defaultprovider);
    }

The above method can be called from within Application_Start in Global.asax or some other place during Unity bootstrapping.

Once that is set up, the System.Web.Http.Filters.ActionFilterAttribute has it's properties injected.

~~
Randy Levy
entlib.support@live.com
Enterprise Library support engineer
Support How-to
Jul 31, 2014 at 2:17 PM
What is the purpose of the following line?

List<FilterInfo> filterInfoList = new List<FilterInfo>();
Aug 1, 2014 at 4:55 AM
You have a keen eye -- that line is not needed. It must be an artifact left over from either another issue or perhaps a different interim approach. I'll remove it.

Thanks.