【问题标题】:How to use Action Filters with Dependency Injection in ASP.NET CORE?如何在 ASP.NET CORE 中使用具有依赖注入的动作过滤器?
【发布时间】:2017-01-08 10:35:47
【问题描述】:

我在我的ASP.NET CORE 应用程序中到处使用基于构造函数的依赖注入,我还需要在我的操作过滤器中解决依赖关系:

public class MyAttribute : ActionFilterAttribute
{
    public int Limit { get; set; } // some custom parameters passed from Action
    private ICustomService CustomService { get; } // this must be resolved

    public MyAttribute()
    {
    }

    public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        // my code
        ...

        await next();
    }
}

然后在控制器中:

[MyAttribute(Limit = 10)]
public IActionResult()
{
    ...

如果我将 ICustomService 放入构造函数,那么我将无法编译我的项目。那么,我应该如何在动作过滤器中获取接口实例?

【问题讨论】:

标签: c# asp.net asp.net-mvc asp.net-core action-filter


【解决方案1】:

如果您想避免使用服务定位器模式,您可以通过 TypeFilter 的构造函数注入来使用 DI。

在你的控制器中使用

[TypeFilter(typeof(MyActionFilterAttribute), Arguments = new object[] {10})]
public IActionResult() NiceAction
{
   ...
}

您的ActionFilterAttribute 不再需要访问服务提供者实例。

public class MyActionFilterAttribute : ActionFilterAttribute
{
    public int Limit { get; set; } // some custom parameters passed from Action
    private ICustomService CustomService { get; } // this must be resolved

    public MyActionFilterAttribute(ICustomService service, int limit)
    {
        CustomService = service;
        Limit = limit;
    }

    public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        await next();
    }
}

对我来说注释[TypeFilter(typeof(MyActionFilterAttribute), Arguments = new object[] {10})]似乎很尴尬。为了得到像[MyActionFilter(Limit = 10)]这样更易读的注解,你的过滤器必须继承自TypeFilterAttribute。我对How do I add a parameter to an action filter in asp.net? 的回答展示了这种方法的一个示例。

【讨论】:

  • 如果你需要异步,你也可以使用IAsyncActionFilter而不是IActionFilter
【解决方案2】:

你可以使用Service Locator:

public void OnActionExecuting(ActionExecutingContext actionContext)
{
     var service = actionContext.HttpContext.RequestServices.GetService<IService>();
}

请注意,泛型方法 GetService&lt;&gt; 是一个扩展方法,位于命名空间 Microsoft.Extensions.DependencyInjection 中。

如果你想使用构造函数注入,请使用TypeFilter。见How do I add a parameter to an action filter in asp.net?

【讨论】:

  • 这是一个很好的建议,但是它需要更多的工作来进行单元测试。
  • 为我工作 - 当我的类 'ApiKeyAuthAttribute : Attribute, IAsyncActionFilter' 使用构造函数注入时,我无法装饰我的控制器 '[ApiKeyAuth(thingToInject)]' 也不能使用 '[ServiceFilter(typeof( ApiKeyAuthAttribute))]',而使用上述方法完全避免构造函数注入并直接从 RequestServices 中获取已解析的 thingToInject 实现。
【解决方案3】:

您可以使用 ServiceFilters 在控制器中实例化您需要的 ActionFilters。

在控制器中:

[ServiceFilter(typeof(TrackingAttribute), Order = 2)]

你需要在依赖容器中注册TrackingAttribute,这样ServiceFilter才能解析它。

https://www.strathweb.com/2015/06/action-filters-service-filters-type-filters-asp-net-5-mvc-6/了解更多信息

【讨论】:

    【解决方案4】:

    这样做是一个不错的选择(在 .NET Core 3.1 中测试)

    1. 在 Filter 类里面放这个:

      公共静态类FilterContainer {

           public class GenericFilter : ActionFilterAttribute
           {
               public override void OnActionExecuting(ActionExecutingContext filterContext)
               {
                   string Action = filterContext.ActionDescriptor.RouteValues["action"];
                   Console.WriteLine($"[action]: {Action} STARTING");
               }
      
               public override void OnActionExecuted(ActionExecutedContext filterContext)
               {
                   string Action = filterContext.ActionDescriptor.RouteValues["action"];
                   Console.WriteLine($"[action]: {Action} FINISHED");
               }
      
               public override void OnResultExecuting(ResultExecutingContext filterContext)
               {
                   string Action = filterContext.ActionDescriptor.RouteValues["action"];
                   Console.WriteLine($"[action]: {Action} GIVING RESULT");
               }
      
               public override void OnResultExecuted(ResultExecutedContext filterContext)
               {
                   string Action = filterContext.ActionDescriptor.RouteValues["action"];
                   ObjectResult ObjectResult = (ObjectResult)filterContext.Result;
                   Console.WriteLine($"[action]: {Action} RESULT GIVEN. Value: {ObjectResult.Value}");
               }
           }
       }
      
    2. 在 Startup.cs/ConfigureServices(IServiceCollection services) 里面放这个:

      services.AddControllers().AddMvcOptions(options => options.Filters.Add(new FilterContainer.GenericFilter()));

    结果是,对 .NET Core 应用程序中任何类型的操作的请求都将通过此管道进出,而无需在任何操作之上声明过滤器属性。

    让我在 Visual Studio 的“输出”窗口中向您展示一个示例:

    [action]: JSON STARTING
    [action]: JSON FINISHED
    [action]: JSON GIVING RESULT
    [action]: JSON RESULT GIVEN. Value: TestId: 103, FullName:...
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-04
      • 2016-09-27
      • 2018-01-16
      • 2019-02-22
      • 1970-01-01
      相关资源
      最近更新 更多