如何使用属性的依赖注入?

The*_*Sky 53 .net c# asp.net-mvc dependency-injection

在我正在创建的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.
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

所以问题显然是我的authorize属性依赖于ASPNETAuthorizationService我创建的属性.我不能去构造函数方式,因为属性是编译时检查的.

有一点需要提及的是,我正在使用自己制作的小IoC,并且它还没有支持属性注入(尚未).当然,如果我确实去了房产注入路线,我必须增加对它的支持(我必须做一些研究).

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

Str*_*ior 11

我原本以为这是不可能的,但我会纠正.这是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现在允许您根据特定属性的存在将绑定添加到特定过滤器,代码如下:

// 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);
Run Code Online (Sandbox Code Playgroud)

这符合Mark SeemanSimple Injector的作者所提出的原则,即你应该将动作过滤器的逻辑与自定义属性类分开.

MVC 5和6也使得将值注入属性变得比以前容易得多.尽管如此,将动作过滤器与属性分离仍然是最好的方法.


Sha*_*tin 9

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

严格地说,我们不能使用依赖注入将依赖注入属性.属性用于元数据而不是行为.[AttributeSpecification()]通过禁止引用类型作为参数来鼓励这一点.

您可能正在寻找的是一起使用属性和过滤器,然后将依赖项注入过滤器.该属性添加元数据,用于确定是否应用过滤器,过滤器接收注入的依赖项.

如何使用属性的依赖注入?

这样做的理由很少.

也就是说,如果您打算注入属性,可以使用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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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
    }
}
Run Code Online (Sandbox Code Playgroud)

使用可以接收依赖项的公共setter创建属性.

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

using Microsoft.AspNetCore.Mvc;

[Route("api/[controller]")]
[MyAttribute("SomeArgument")]
public class ValuesController : Controller
{
    [HttpGet]
    [MyAttribute("AnotherArgument")]
    public string Get()
    {
        return "Foobar";
    }
}
Run Code Online (Sandbox Code Playgroud)

上面演示了一种方法,对于罕见的用例,您可以将依赖项注入属性.如果您找到了这样做的正当理由,请在评论中发布.

  • 非常感谢 - 我的用例(我不确定它是否正确)是将依赖项传递给验证器属性。我们使用属性来验证我们的模型,并且一些验证逻辑需要我们隔离到注入服务中的共享逻辑。 (2认同)