属性中的依赖注入

时间:2015-04-28 09:14:35

标签: c# asp.net-mvc authentication dependency-injection unity-container

我正在尝试将依赖项注入自定义AuthorizeAttribute,如下所示:

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

这有效,但似乎是作为单身人士解决,这意味着我得到了pervious question

中描述的问题

我想要做的是使用属性注入,但由于我的属性本身未被Unity解析,我无法找到配置容器来拦截和解析属性的方法。我尝试过以下方法:

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.RegisterType<UserCanAccessArea>(new InjectionProperty("permissionService"));

但是该属性在运行时始终为null。

有没有人实现过这个目标?如果有,你有一个例子吗?

3 个答案:

答案 0 :(得分:41)

您应该防止完全依赖注入属性。本文解释了原因:Dependency Injection in Attributes: don’t do it!。总之,文章解释说:

  • 无法进行构造函数注入,因为无法拦截Attribute实例的创建; CLR处于控制之中。
  • 使用属性注入是脆弱的,因为它会导致Temporal Coupling,这应该被阻止。
  • 依赖注入属性使得无法验证 容器配置的正确性。
  • 像MVC和Web API缓存属性这样的框架,很容易意外地创建导致错误的captive dependencies

你有两个选择:

  1. 按照Mark Seemann的referenced articlethis related article中的说明,将数据(属性)与其行为(服务)分开,使属性成为被动。
  2. humble objects中的说明将您的属性转换为this answer。这意味着你:
    1. 将属性中的所有逻辑提取到包含所有依赖项的自定义服务中。
    2. 在您的容器中注册该服务。
    3. 让属性的方法(在你的情况下为AuthorizeCore)除了从服务定位器/ DependencyResolver解析服务并调用服务的方法之外什么都不做。这里要注意的重要一点是,你不能做构造函数注入,属性注入和服务不能存储在属性私有状态(正如你已经注意到的)。
  3. 使用哪个选项:

    • 如果您非常热衷于保持设计清洁,或者您需要以这种方式应用多个属性,或者您想要应用属性,则在使用选项1时不会使用选项1依赖于System.Web.Mvc。
    • 否则请使用选项2.

答案 1 :(得分:1)

ASP.NET Core 中,现在可以通过创建自定义属性,实现IFilterFactory或使用TypeFilterAttribute以及{{3} }。

两者都实现了IFilterFactory,并且执行了在实现IFilterFactory的自定义属性中通常要做的事情,唯一的区别是它们都支持排序(可以根据需要在自定义属性中添加)

但更具体地说-ServiceFilterAttribute从实际服务集合中获取过滤器的实例,这使您可以为其定义特定的生存期,而TypeFilterAttribute并不使用服务集合来创建您的过滤器对象,它使用ServiceFilterAttribute方法的结果Microsoft.Extensions.DependencyInjection.ObjectFactory。 (基本上,它创建了带有很多表达式树的对象。)TypeFilterAttribute还允许您传递非服务构造函数参数的参数。两者都将服务集合用于任何DI。

对于您现有的代码库,您可以非常简单地执行以下任一操作以在属性的构造函数中实现依赖注入:

  • [TypeFilter(typeof(MyExistingFilterWithConstructorDI))]
  • [TypeFilter(typeof(MyExistingFilterWithConstructorDIAndParams), Arguments = new object[] { "first non-service param", "second non-service param" })]
  • [ServiceFilter(typeof(MyExistingFilterWithConstructorDI)) (您需要在适当的生命周期内将过滤器注册到服务集合中)

现在,就性能而言,如果最终使用TypeFilterAttribute,将如上所述创建带有表达式树的过滤器类型,而如果您仅创建自己的IFilterFactory ,您可以控制该部分,即只需实例化对象,并满足任何依赖项注入需求-您可以将提供的IServiceProvider用作接口CreateInstance方法的一部分。

作为IsReusable界面一部分的IFilterFactory属性可让您显示是否首选框架以在请求范围之外使用对象。这绝不能保证您永远只为过滤器使用单个对象。

答案 2 :(得分:-3)

警报警报警报警报! XD 我是这样实现的:

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

        return;
    }
}