37

I am trying to inject a dependency into a custom AuthorizeAttribute as follows:

public class UserCanAccessArea : AuthorizeAttribute
{
    readonly IPermissionService permissionService;

    public UserCanAccessArea() :
        this(DependencyResolver.Current.GetService<IPermissionService>()) { }

    public UserCanAccessArea(IPermissionService permissionService)
    {
        this.permissionService = permissionService;
    }

    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        string AreaID =
            httpContext.Request.RequestContext.RouteData.Values["AreaID"] as string;

        bool isAuthorized = false;

        if (base.AuthorizeCore(httpContext))
            isAuthorized = permissionService.UserCanAccessArea(AreaID, httpContext.User);

        return isAuthorized;
    }
}

This works but seems to be resolving as a singleton meaning I get the problems described in my pervious question

What I'd like to do is use property injection but as my Attribute itself is not resolved by Unity I'm unable to find a way to configure the container to intercept and resolve a property. I have tried the following:

public class UserCanAccessArea : AuthorizeAttribute
{
    public IPermissionService permissionService { get; set; }

    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        string AreaID =
            httpContext.Request.RequestContext.RouteData.Values["AreaID"] as string;

        bool isAuthorized = false;

        if (base.AuthorizeCore(httpContext))
            isAuthorized = permissionService.UserCanAccessArea(AreaID, httpContext.User);

        return isAuthorized;
    }
}

Container:

container.RegisterType<UserCanAccessArea>(new InjectionProperty("permissionService"));

But the property is always null at runtime.

Has anyone achieved this and if so do you have an example?

Steven
  • 166,672
  • 24
  • 332
  • 435
James
  • 1,979
  • 5
  • 24
  • 52

3 Answers3

72

You should prevent doing dependency injection into attributes completely. The reason for this is explained in this article: Dependency Injection in Attributes: don’t do it!. In summary the article explains that:

  • Constructor injection is not possible, because creation of an Attribute instance cannot be intercepted; the CLR is in control.
  • The use of property injection is fragile, since it results in Temporal Coupling, which should be prevented.
  • Dependency injection into attributes makes it impossible to verify the correctness of the container's configuration.
  • Frameworks like MVC and Web API cache attributes, making it very easy to accidentally create captive dependencies causing bugs.

You have two choices here:

  1. Make the attributes passive, by splitting the data (the attribute) from its behavior (the service) as explained in the referenced article and this related article from Mark Seemann.
  2. Turn your attributes into humble objects as explained in this answer. This means you:
    1. extract all logic from the attribute into a custom service that contains all dependencies.
    2. Register that service in your container.
    3. let the attribute's method (AuthorizeCore in your case) do nothing more than resolving the service from the service locator / DependencyResolver and call the service's method. Important to note here is that you cannot do constructor injection, property injection and the service cannot be stored in the attributes private state (as you already noticed).

Which option to use:

  • Use option 1 if you are very keen into keeping your design clean, or you have more than a few attributes that you need to apply this way, or you want to apply attributes are defined in an assembly that doesn't depend on System.Web.Mvc.
  • Use option 2 otherwise.
Steven
  • 166,672
  • 24
  • 332
  • 435
  • I have been exploring option1, and seems that this can't be used if you have specified for the dbcontext to be resolved InRequestScope (Ninject). Otherwise works perfectly. I have tried it firstly with service locator (anti-pattern, but solves the creation of service objects). The issue with custom authorized attribute is that its been created at runtime and doesn't follow InRequestScope. Please correct me if I am wrong. – Lyubomir Velchev May 01 '15 at 11:43
  • I want to use InRequestScope option, because want to have one and the same dbcontext when working with different repositories, and be able to use UnitOfWork - one place to call savechanges in the dbcontext. – Lyubomir Velchev May 01 '15 at 11:45
  • One idea is not to use DI for the service, but just create an actual object used in the Authorization filter. Is that a good approach? It will make my code ugly as the service which I want have other dependant services and repositories.... – Lyubomir Velchev May 01 '15 at 11:50
  • 1
    @LyubomirVelchev: Please read [the article](https://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=98) referred to in option 1 again closely. The lifestyle of your DbContext is irrelevent in that case. The attributes can be singletons without any problem, because you don't inject anything into the attribute. The attribute is just data. But (as always) you have to make sure that your action filter services have a lifetime that is shorter than or equals to its dependencies, because you'll got [captive dependencies](http://blog.ploeh.dk/2014/06/02/captive-dependency/). – Steven May 01 '15 at 11:54
  • 2
    Its very important to resolve the dependency in the AuthorizeCore, and not store an instance of the dependency with the Attribute. – Keith Sirmons Jul 03 '15 at 02:50
  • 1
    @Steven - You can avoid captive dependencies by [using `IFilterProvider` to resolve the filter on demand from the DI container](http://stackoverflow.com/a/36224308/181087) rather than registering the filter in the static `GlobalFilterCollection`. – NightOwl888 Jan 23 '17 at 18:36
9

In ASP.NET Core, this is now possible by either creating a custom attribute, implementing IFilterFactory, or by using TypeFilterAttribute, as well as ServiceFilterAttribute.

Both implement IFilterFactory and do what you'd normally do in your custom attribute implementing IFilterFactory, the only difference is that they support ordering (which you can add if you wish in your custom attribute).

But more specifically - ServiceFilterAttribute gets the instance of your filter from the actual service collection, which allows you to define a specific lifetime to it, whereas TypeFilterAttribute does not use the service collection to create your object, it uses Microsoft.Extensions.DependencyInjection.ObjectFactory which is the result of CreateFactory method. (Basically it creates your object with a lot of expression trees.) TypeFilterAttribute also allows you to pass arguments for non-service constructor parameters. Both use the service collection for any DI.

For your existing codebase you can quite simply do any of the following to achieve Dependency injection in the constructor of an attribute:

  • [TypeFilter(typeof(MyExistingFilterWithConstructorDI))]
  • [TypeFilter(typeof(MyExistingFilterWithConstructorDIAndParams), Arguments = new object[] { "first non-service param", "second non-service param" })]
  • [ServiceFilter(typeof(MyExistingFilterWithConstructorDI)) (you will need to register your filter into the service collection with an appropriate lifetime)

Now, as far as performance goes, if you end up using TypeFilterAttribute, the type for your filter will be created as mentioned above, with expression trees, whereas if you simply create your own IFilterFactory, you control that part, i.e you simply instantiate your object, and for any dependency injection needs - you use the provided IServiceProvider as part of the CreateInstance method for the interface.

The IsReusable property, as part of the IFilterFactory interface is there for you to show if you prefer the framework to use your object outside of the request scope. This, in no way guarantees that you'll ever be stuck with a single object for your filter.

SpiritBob
  • 2,355
  • 3
  • 24
  • 62
-1

I implemented it like this:

public class ClaimsHandlerAttribute : AuthorizeAttribute, IAuthorizationFilter
{  
    public void OnAuthorization(AuthorizationFilterContext context)
    { 
        var jwtAuthManager =
            context.HttpContext.RequestServices.GetService(typeof(IJwtAuthManager))
                as JwtAuthManager; 

        return;
    }
}
Steven
  • 166,672
  • 24
  • 332
  • 435