asp.net-mvcdependency-injectionfilterautofac

How do I resolve Dependency Injection in MVC Filter attributes


I have a custom attribute class derived from AuthorizationAttribute, which performs custom security on controller actions. The OnAuthorizationCore method depends on various other components (e.g. DAL) in order to ajudicate whether a user can invoke an action.

I'm using Autofac for dependency injection. The ExtensibleActionInvoker claims to be able to perform property injection on action filters. Setting an attribute's properties at runtime (which seems like a bad idea) will work in a simple unit test, but in a busy, multi-threaded web server it's bound to go wrong, and so this idea seems like an anti-pattern. Hence this question:

If my AuthorizationAttribute depends on other components in order to work correctly, what it the right [architecture] pattern in order to achieve this?

i.e. AuthorizationAttribute depends on IUserRepository... how should this relationship be resolved?


Solution

  • The ExtensibleActionInvoker claims to be able to perform property injection on action filters.

    Correct - but don't confuse action filters with the attributes that might not implement them. The cleanest way to approach this in ASP.NET MVC is to split responsibilities, even though the MVC framework allows you to combine them.

    E.g., use a pair of classes - an attribute class that holds data only:

    // Just a regular old attribute with data values
    class SomeAttribute : Attribute { ... }
    

    And a filter that has dependencies injected:

    // Gets dependencies injected
    class SomeFilter : IActionFilter { ... }
    

    SomeFilter just uses the typical approach of getting the SomeAttribute attribute from the controller or action method via GetCustomAttributes() to do whatever work is needed.

    You can then use ExtensibleActionInvoker to wire up the filter:

    builder.RegisterControllers(...).InjectActionInvoker();
    builder.RegisterType<ExtensibleActionInvoker>().As<IActionInvoker>();
    builder.RegisterType<SomeFilter>().As<IActionFilter>();
    

    It might be a little more code than you'd write using the attribute-as-filter approach, but the quality of the code will be better in the long run (e.g. by avoiding the limitations of attributes and the awkwardness of the Service Locator solutions.)