属性中的依赖注入

问题描述 投票:0回答:3

我正在尝试将依赖项注入自定义

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

这有效,但似乎是作为一个单例来解决的,这意味着我遇到了我的上一个问题

中描述的问题

我想做的是使用属性注入,但由于我的属性本身没有被 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"));

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

有人实现了这一点吗?如果是的话,你有一个例子吗?

c# asp.net-mvc authentication dependency-injection unity-container
3个回答
74
投票

您应该完全防止对属性进行依赖注入。本文解释了其原因:属性中的依赖注入:不要这样做!。文章总结道:

  • 构造函数注入是不可能的,因为属性实例的创建不能被拦截; CLR 处于控制之中。
  • 使用属性注入很脆弱,因为它会导致时间耦合,应该避免这种情况。
  • 对属性的依赖注入导致无法验证 容器配置的正确性。
  • MVC 和 Web API 等框架缓存属性,很容易意外创建强制依赖项,从而导致错误。

您有两个选择:

  1. 通过将数据(属性)与其行为(服务)分开,使属性成为被动属性,如 Mark Seemann 的参考文章此相关文章中所述。
  2. 将你的属性变成不起眼的对象,如这个答案中所述。这意味着你:
  3. 将属性中的所有逻辑提取到包含所有依赖项的自定义服务中。
  4. 在您的容器中注册该服务。
  5. 让属性的方法(在您的情况下为
    AuthorizeCore
    )只执行从服务定位器/DependencyResolver 解析服务并调用服务的方法。这里需要注意的是,您不能进行构造函数注入、属性注入,并且服务不能存储在属性私有状态中(正如您已经注意到的那样)。

使用哪个选项:

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

10
投票

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

两者都实现

IFilterFactory
并执行您通常在实现
IFilterFactory
的自定义属性中执行的操作,唯一的区别是它们支持排序(如果您愿意,可以在自定义属性中添加)。

但更具体地说 -

ServiceFilterAttribute
从实际服务集合中获取过滤器的实例,这允许您为其定义特定的生命周期,而
TypeFilterAttribute
不使用服务集合来创建对象,它使用 Microsoft .Extensions.DependencyInjection.ObjectFactory 这是 CreateFactory 方法的结果。 (基本上,它使用大量表达式树创建对象。)
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
接口的一部分,可用于显示您是否更喜欢框架在请求范围之外使用您的对象。这决不能保证您的过滤器永远不会被单一对象所困扰。


0
投票

我是这样实现的:

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

        return;
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.