属性中的依赖注入

Jam*_*mes 19 c# authentication asp.net-mvc 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这有效,但似乎是作为一个单身人士解决,这意味着我得到了我以前的问题中描述的问题

我想要做的是使用属性注入但由于我的属性本身没有被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;
    }
}
Run Code Online (Sandbox Code Playgroud)

容器:

container.RegisterType<UserCanAccessArea>(new InjectionProperty("permissionService"));
Run Code Online (Sandbox Code Playgroud)

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

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

Ste*_*ven 43

您应该防止完全依赖注入属性.本文解释了这一点的原因:属性中的依赖注入:不要这样做!.总之,本文解释说:

  • 构造函数注入是不可能的,因为不能拦截Attribute实例的创建; CLR处于控制之中.
  • 使用属性注入是脆弱的,因为它会导致时间耦合,应该加以防止.
  • 依赖注入属性使得无法验证容器配置的正确性.
  • 像MVC和Web API缓存属性这样的框架,使得很容易意外地创建导致错误的强制依赖.

你有两个选择:

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

使用哪个选项:

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

  • 解析AuthorizeCore中的依赖关系,而不是将依赖关系的实例与Attribute存储在一起,这一点非常重要。 (2认同)

Sup*_*Bob 5

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界面的一部分供您显示,如果您希望框架在请求范围之外使用您的对象。这绝不能保证您的过滤器会被单个对象卡住。

  • 不幸的是,AuthorizeAttribute 没有实现 IFilterMetadata。 (2认同)