将依赖项注入类库中的AuthorizeAttribute

Sha*_*thy 7 .net c# dependency-injection asp.net-mvc-3

我遇到了一个有趣的设计问题,我正在写一个类库.我有一个AuthorizeAttribute的自定义实现,我希望客户能够像这样使用:

[Protected("permission_name")] 
Run Code Online (Sandbox Code Playgroud)

在上面的代码中,PermissionAttribute继承自AuthorizeAttribute并使用本地默认值(使用HttpContext创建的DefaultContext).

在幕后,该属性使用SecurityService来检查用户,角色和权限(SecurityService本身使用客户端提供的持久性服务,他们可以在应用程序的组合根中连接).

所以我的属性需要引用SecurityService才能运行.由于Attribute构造函数只能有编译时常量,所以我不能使用构造函数注入.

我不想强迫我的客户使用DI框架 - 如果他们愿意,他们应该能够在不使用IoC库的情况下发现连接其组成根中的必要依赖项.

以下是我的选择:

  • 让库使用单一的SecurityService.
  • 使用属性注入,这可以工作但是
    1. 它会使依赖似乎是可选的,它不是和
    2. 我不知道在一个授权属性的MVC应用程序中我可以在哪里进行属性注入.

上面2.的一个可能的解决方案是在应用程序启动时将SecurityService的实例设置为属性的静态属性,并使用guard子句来阻止它被设置多次,如下所示:

class ProtectedAttribute : ...
{
    private static ISecurityService _SecurityService ;
    public static ISecurityService SecurityService
    {
        get 
        {
            return _SecurityService ;
        }
        set 
        {
            if (_SecurityService != null)
                throw new InvalidOperationException("You can only set the SecurityService once per lifetime of this app.") ;
            _SecurityService = value ;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

SecurityService可以是抽象服务外观,以便可以通过不同的实现进行扩展/替换.

有没有更好的方法来解决这个问题?

更新:添加一些代码来说明我将如何做:

在返回权限名称的属性上添加公共属性:

public class ProtectedAttribute : ...
{
  private string _Permission ;
  public string Permission { get { return _Permission ; } /*...*/ }

  public ProtectedAttribute(string permission) { /*...*/ }
}
Run Code Online (Sandbox Code Playgroud)

设置授权过滤器并通过Ninject配置依赖关系(如果使用Ninject):

using Ninject.Web.Mvc.FilterBindingSyntax;

public class MyModule : Ninject.Modules.NinjectModule
{
  public override void Load()
  {
    // mySecurityService instance below can have a singleton lifetime - perfect!
    this.BindFilter<MyAuthorizationFilter>(FilterScope.Action, 0)
        .WhenActionMethodHas<ProtectedAttribute>()
        .WithConstructorArgument("securityService", mySecurityService)
        .WithConstructorArgumentFromActionAttribute<ProtectedAttribute>("permission", p => p.PermissionName) ;
  }
}
Run Code Online (Sandbox Code Playgroud)

哦,它是......美丽的鼻涕

Dar*_*rov 7

使用ASP.NET MVC 3,您可以使用构造函数注入和动作过滤器,这要归功于新的IFilterProvider.这样,您不再需要使用操作过滤器来装饰控制器操作.您可以使用此界面并使用标记属性来应用它们.

如果您不想手动实现它,您可以始终使用现有的DI框架,例如Ninject,它提供了一种流畅的方式来定义动作过滤器依赖项.