C# 将依赖项注入类库中的AuthorizeAttribute
我在写一个类库时遇到了一个有趣的设计问题。我有一个authorized属性的自定义实现,我希望客户端能够像这样使用它: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
[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应用程序中,在授权属性上哪里可以进行属性注入
[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
,提供了非常干净的解决方案。类似的问题(温莎城堡)。