4

Following up on Authorization Filter Dependency Injection with ASP.New MVC 4 Web Api . Is there a way to use dependency injection on filters that are set globally on all controller classes:

config.Filters.Add(new WebApplicationApiAuthorizeAttribute());  

It looks like the GetFilters method in the ActionDescriptorFilterProvider only works on method level filters.

public class UnityWebApiFilterAttributeFilterProvider : ActionDescriptorFilterProvider,
    System.Web.Http.Filters.IFilterProvider
{
private readonly IUnityContainer _container;

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

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

    this.BuildUpAttributes(filters);

    return filters;
}

private void BuildUpAttributes(IEnumerable filterInfo)
{
    foreach (FilterInfo filter in filterInfo)
    {
        object o = _container.BuildUp(filter.GetType(), filter);
    }
}
}
Community
  • 1
  • 1
Agreene
  • 508
  • 7
  • 16
  • 1
    Great question. I was going to answer it, having touched on the topic so many times before, but I still don't feel like I have an elegant solution. For now, check out [Injecting dependencies into ASP.NET MVC 3 action filters. What's wrong with this approach?](http://stackoverflow.com/questions/7192543/injecting-dependencies-into-asp-net-mvc-3-action-filters-whats-wrong-with-this/7194467#7194467) and hopefully [Mark Seemann](http://stackoverflow.com/users/126014/mark-seemann) will spot this question. – Rowan Freeman Mar 27 '14 at 00:24
  • So @RowanFreeman, based on your comment I'm not clear on whether I have to use the ServiceLocator in order to inject a class level depencency. Is there another way or am I stuck using the anti-pattern mentioned in the article you linked to? – Agreene Mar 27 '14 at 00:39
  • For what it's worth, a **ServiceLocator** is what I went up with in my project. I don't know of any other elegant way to inject dependencies into global filters. **Thinking out loud:** *I wonder if you could create a new filter interface (eg. `IInjectableActionFilter`) and then use a factory to create them. The factory would actually be dealt with by the IoC container in the same way that they often deal with Controller factories. Then the container would be responsible for creating the filter, applying it and injecting dependencies.* – Rowan Freeman Mar 27 '14 at 00:43

1 Answers1

5

If you want these global filters to get injected, you will have to resolve them from the container and add them to the filters collection:

GlobalFilters.Filters.Add(container.Resolve<MyFilter>());

Or do something like:

var filter = WebApplicationApiAuthorizeAttribute();
container.BuildUp(filter.Gettype(), filter);
GlobalFilters.Filters.Add(filter);

But one big warning about using global filters. Global filters are... global. Or in IoC terminology: they are singletons. This means that all its dependencies will effectively become singletons as well, which might cause all sorts of concurrency bugs when they are not expected to live for the duration of the application.

So you should only do this when all the filter's direct and indirect dependencies are singletons, which is great if you can do this, but often isn't the case. So another option is to create a proxy that allows resolving the real instance on the fly:

public sealed class UnityActionFilterProxy<TActionFilter> : IActionFilter
    where TActionFilter : IActionFilter
{
    private readonly IUnityContainer container;
    public UnityActionFilterProxy(IUnityContainer container) {
        this.container = container;
    }

    public Task<HttpResponseMessage> ExecuteActionFilterAsync(HttpActionContext context,
        CancellationToken token, Func<Task<HttpResponseMessage>> continuation) {
        return this.container.Resolve<TActionFilter>().ExecuteActionFilterAsync(
            context, token, continuation);
    }

    public bool AllowMultiple { get { return false; } }
}

This proxy can be injected as singleton in the global filters collection as follows:

GlobalFilters.Filters.Add(
    container.Resolve<UnityActionFilterProxy<MyFilter>>());

The global filters isn't the only place in Web API where the design is a bit... smelly. Take a look at this related question about DelegatingHandlers.

Rosdi Kasim
  • 24,267
  • 23
  • 130
  • 154
Steven
  • 166,672
  • 24
  • 332
  • 435
  • do you know of any good articles describing the types of concurrency issues I might run into if I use a global filter? – Agreene Mar 28 '14 at 18:32
  • @AdamGreene: Take a look at [this Q/A](https://stackoverflow.com/questions/3266295/net-entity-framework-and-transactions). It's about the lifetime of a `DbContext`. If you inject a `DbContext` into a service that gets referenced by your global filter, you're pretty much screwed. – Steven Mar 28 '14 at 23:17