如何使用dependency injection的属性?

在我创build的MVC项目中,我有以下RequirePermissionAttribute ,它被放在需要特定权限的任何操作上(这个例子已经简化了):

 public class RequirePermissionAttribute : ActionFilterAttribute, IAuthorizationFilter { public Operation Permissions { get; set; } public RequirePermissionAttribute() { } public RequirePermissionAttribute(Operation permissions) { this.Permissions = permissions; } public bool AuthorizeCore(HttpContextBase httpContext) { IAuthorizationService authServ = new ASPNETAuthorizationService(); return authServ.Authorize(httpContext); } public void OnAuthorization(AuthorizationContext filterContext) { Enforce.ArgNotNull(filterContext); if (this.AuthorizeCore(filterContext.HttpContext)) { // code snipped. } else { // code snipped. } } } 

所以显然这个问题是,我的授权属性依赖于我创build的ASPNETAuthorizationService 。 由于属性是编译时检查的,所以我不能使用构造函数。

有一点要提到,我使用了我自己制作的小型IoC,它不支持属性注入(还)。 当然,如果我真的走了财产注入路线,我不得不支持它(我不得不做一些研究)。

将某些东西注入到属性类中的最佳方法是什么?

我原本以为这是不可能的,但是我纠正了。 下面是Ninject的一个例子:

http://codeclimber.net.nz/archive/2009/02/10/how-to-use-ninject-to-inject-dependencies-into-asp.net-mvc.aspx

更新2016-10-13

到现在为止,这是一个相当古老的问题,框架已经发生了很大的变化。 Ninject现在允许您添加绑定到特定的filter的基础上存在的具体属性,代码是这样的:

 // LogFilter is applied to controllers that have the LogAttribute this.BindFilter<LogFilter>(FilterScope.Controller, 0) .WhenControllerHas<LogAttribute>() .WithConstructorArgument("logLevel", Level.Info); // LogFilter is applied to actions that have the LogAttribute this.BindFilter<LogFilter>(FilterScope.Action, 0) .WhenActionHas<LogAttribute>() .WithConstructorArgument("logLevel", Level.Info); // LogFilter is applied to all actions of the HomeController this.BindFilter<LogFilter>(FilterScope.Action, 0) .WhenControllerTypeIs<HomeController>() .WithConstructorArgument("logLevel", Level.Info); // LogFilter is applied to all Index actions this.BindFilter(FilterScope.Action, 0) .When((controllerContext, actionDescriptor) => actionDescriptor.ActionName == "Index") .WithConstructorArgument("logLevel", Level.Info); 

这符合Mark Seeman和Simple Injector的作者所说的原则,即你应该将你的动作filter的逻辑与自定义属性类分开。

MVC 5和6也使得向属性注入值比以前更容易 。 不过,将你的动作filter与你的属性分开是最好的方法。

将某些东西注入到属性类中的最佳方法是什么?

严格来说,我们不能使用dependency injection来将dependency injection到属性中。 属性是元数据而不是行为。 [AttributeSpecification()]鼓励通过禁止引用types作为参数。

你可能要找的是一起使用一个属性和一个filter,然后注入依赖到filter 。 该属性添加元数据,该元数据确定是否应用filter,并且filter接收注入的依赖项。

如何使用dependency injection的属性?

这样做的原因很less。

也就是说,如果你打算注入一个属性,你可以使用ASP.NET Core MVC IApplicationModelProvider 。 框架将依赖关系传递给提供者的构造函数,提供者可以将依赖关系传递给属性的属性或方法。

在您的启动中,注册您的提供者。

 using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Mvc.ApplicationModels; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; public class Startup { public void ConfigureServices(IServiceCollection services) { services.TryAddEnumerable(ServiceDescriptor.Transient <IApplicationModelProvider, MyApplicationModelProvider>()); services.AddMvc(); } public void Configure(IApplicationBuilder app) { app.UseMvc(); } } 

在提供程序中使用构造函数注入,并将这些依赖项传递给该属性。

 using System.Linq; using Microsoft.AspNetCore.Mvc.ApplicationModels; using Microsoft.AspNetCore.Mvc.Routing; public class MyApplicationModelProvider : IApplicationModelProvider { private IUrlHelperFactory _urlHelperFactory; // constructor injection public MyApplicationModelProvider(IUrlHelperFactory urlHelperFactory) { _urlHelperFactory = urlHelperFactory; } public int Order { get { return -1000 + 10; } } public void OnProvidersExecuted(ApplicationModelProviderContext context) { foreach (var controllerModel in context.Result.Controllers) { // pass the depencency to controller attibutes controllerModel.Attributes .OfType<MyAttribute>().ToList() .ForEach(a => a.UrlHelperFactory = _urlHelperFactory); // pass the dependency to action attributes controllerModel.Actions.SelectMany(a => a.Attributes) .OfType<MyAttribute>().ToList() .ForEach(a => a.UrlHelperFactory = _urlHelperFactory); } } public void OnProvidersExecuting(ApplicationModelProviderContext context) { // intentionally empty } } 

使用可以接收依赖关系的公共setter创build一个属性。

 using System; using Microsoft.AspNetCore.Mvc.Routing; public sealed class MyAttribute : Attribute { private string _someParameter; public IUrlHelperFactory UrlHelperFactory { get; set; } public MyAttribute(string someParameter) { _someParameter = someParameter; } } 

将该属性应用于控制器或操作。

 using Microsoft.AspNetCore.Mvc; [Route("api/[controller]")] [MyAttribute("SomeArgument")] public class ValuesController : Controller { [HttpGet] [MyAttribute("AnotherArgument")] public string Get() { return "Foobar"; } } 

上面演示了一种罕见用例的方法,即可以将依赖关系注入到属性中。 如果你找出一个合理的理由来做这件事,请把它张贴在评论中。