C# 将依赖项注入类库中的AuthorizeAttribute

C# 将依赖项注入类库中的AuthorizeAttribute,c#,.net,asp.net-mvc-3,dependency-injection,C#,.net,Asp.net Mvc 3,Dependency Injection,我在写一个类库时遇到了一个有趣的设计问题。我有一个authorized属性的自定义实现,我希望客户端能够像这样使用它: [Protected("permission_name")] class ProtectedAttribute : ... { private static ISecurityService _SecurityService ; public static ISecurityService SecurityService { get

我在写一个类库时遇到了一个有趣的设计问题。我有一个authorized属性的自定义实现,我希望客户端能够像这样使用它:

[Protected("permission_name")] 
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 ;
        }
    }
}
在上面的代码中,PermissionAttribute继承自AuthorizeAttribute并使用本地默认值(使用HttpContext创建的DefaultContext)

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

因此,我的属性需要引用SecurityService才能正常工作。因为属性构造函数只能有编译时常量,所以我不能使用构造函数注入

我不想强迫我的客户使用DI框架——如果他们愿意的话,他们应该能够在合成根目录中发现并连接必要的依赖项,而无需使用IoC库

以下是我的选择:

  • 让库使用singleton SecurityService
  • 使用属性注入,这会起作用,但是
  • 这会使依赖项看起来是可选的,而实际上不是
  • 我不知道在MVC应用程序中,在授权属性上哪里可以进行属性注入
一个可能的解决方案2。上面是在应用程序启动时将SecurityService实例设置为属性上的静态属性,并使用guard子句防止多次设置它,如下所示:

[Protected("permission_name")] 
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 ;
        }
    }
}
SecurityService可以是一个抽象的服务门面,这样就可以用不同的实现来扩展/替换它

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

更新:添加一些代码以显示我将如何操作:

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

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

  public ProtectedAttribute(string permission) { /*...*/ }
}
通过Ninject设置授权筛选器并配置依赖项(如果使用Ninject):

使用Ninject.Web.Mvc.FilterBindingSyntax;
公共类MyModule:Ninject.Modules.NinjectModule
{
公共覆盖无效负载()
{
//下面的mySecurityService实例可以有一个单独的生命周期-完美!
此.BindFilter(FilterScope.Action,0)
.WhenActionMethodHas()时
.WithConstructorArgument(“securityService”,mySecurityService)
.WithConstructorArgumentFromActionAttribute(“权限”,p=>p.PermissionName);
}
}

Ohhh这是一款漂亮的sniffle

有了ASP.NET MVC 3,由于新的。这样,您就不再需要用动作过滤器装饰控制器动作。由于该接口和标记属性,您可以应用它们


如果你不想手动实现它,你可以使用一个现有的DI框架,比如Ninject,它提供了一个定义动作过滤器依赖项的框架。

我的应用程序继承自一个公开IOC容器的基本应用程序类

public interface IInjectableApplication
    {
        IUnityContainer Container { get; }
    }
然后我有一个基本属性类,它知道这一点

public abstract IocAwareActionFilterAttribute : ActionFilterAttribute{
    protected T ResolveItem<T>(ResultExecutedContext context)
        {
            var app = context.HttpContext.ApplicationInstance as IInjectableApplication;
            if (app == null) { throw new NullReferenceException("Application is not IInjectable."); }

            T c = (T)app.Container.Resolve(typeof(T));

            if (c == null) { throw new NullReferenceException(string.Format("Could not find injected {0}.", typeof(T).FullName)); }
            return c;
        }

}
公共抽象IocAwareActionFilterAttribute:ActionFilterAttribute{
受保护的解析项(ResultExecutedContext上下文)
{
var app=context.HttpContext.ApplicationInstance作为IInjectableApplication;
如果(app==null){抛出新的NullReferenceException(“应用程序不可连接”);}
tc=(T)app.Container.Resolve(typeof(T));
如果(c==null){抛出新的NullReferenceException(string.Format(“找不到注入的{0}.”,typeof(T.FullName));}
返回c;
}
}

虽然这不是真正的注入,但由于属性不是“正常”构造的,这提供了类似的行为。没有理由它不适用于其他IOC

@Darin:是的,要求最低MVC 3是可以接受的。更新了标签。相关:+1用于
IFilterProvider
,提供了非常干净的解决方案。类似的问题(温莎城堡)。