How to use Action Filters with Dependency Injection in ASP.NET CORE?

时间:2016-08-31 18:11:10

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

I use constructor-based dependency injection everywhere in my ASP.NET CORE application and I also need to resolve dependencies in my action filters:

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();
    }
}

Then in Controller:

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

If I put ICustomService to the constructor, then I'm unable to compile my project. So, how do I supossed to get interface instances in action filter?

4 个答案:

答案 0 :(得分:16)

如果您想避免使用服务定位器模式,可以使用构造函数注入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?的回答显示了这种方法的一个例子。

答案 1 :(得分:3)

You can use Service Locator:

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

Note that the generic method GetService<> is an extension method and lives in namespace Microsoft.Extensions.DependencyInjection.

If you want to use constructor injection use TypeFilter. See How do I add a parameter to an action filter in asp.net?

答案 2 :(得分:2)

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

在控制器中:

func mergedDict(dictionaries: [[String: Any]]) -> [String: Int] {
    guard dictionaries.count != 0 else { return [:] }

    var result = [String: Int]()

    dictionaries.forEach { dict in
        var name: String?
        var amount: Int?
        dict.forEach{ arg in
            switch arg.key {
            case "fruit":
                name = arg.value as! String
            case "amount":
                amount = arg.value as! Int
            default:
                fatalError("does not exist.")
            }
        }

        guard let fruit = name else { fatalError("name is nil.") }
        guard let newAmount = amount else { fatalError("amount is nil.") }

        guard let priorAmount = result[fruit] else {
            result[fruit] = newAmount
            return
        }
        result[fruit] = priorAmount + newAmount
    }

    return result
}

您需要在依赖项容器中注册TrackingAttribute,以便ServiceFilter可以解决它。

有关详情,请访问https://www.strathweb.com/2015/06/action-filters-service-filters-type-filters-asp-net-5-mvc-6/

答案 3 :(得分:-1)

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

  1. 在过滤器类中输入:

    公共静态类 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:...