'C# - Custom ActionFilter pass in configuration variables

I have a custom action filter that takes in a property but I need the property to come from my appsettings.json file. I pass my configuration into the controller, but when I try to pass in the "_config.GetValue< string >("myString")" the "_config" is red underlined with the message:

An object reference is required for the non-static field, method, or property 'MyController._config'

Action Filter

public class MyActionFilter : ActionFilterAttribute
{
    public string Property1 { get; set; }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        ...
    }
}

Controller

[ApiController]
[Route("[controller]")]
public class MyController : ControllerBase
{
    private readonly IConfiguration _config;

    public MyController(IConfiguration config) {
        _config = config;
    }

    [Authorize]
    [HttpPost(Constants.ActionName.MyMethod, Name = nameof(MyMethod))]
    [MyActionFilter(Property1 = _config.GetValue<string>("myString"))] // Breaks here!
    public ActionResult<string> MyMethod()
    {
        ...
    }
}

How can I do this? Or at least, how can I avoid hardcoding a value for my action filter properties?



Solution 1:[1]

Your current approach does not work because constructor parameters and properties of attributes are evaluated at compile time.

You could use the service locator pattern in your filter like so:

public override void OnActionExecuting(ActionExecutingContext filterContext)
{
    var config = filterContext.HttpContext.RequestServices.GetService<IConfiguration>();
    string Property1 = config.GetValue<string>("myString");
}

However, this approach is debatable because the service locator pattern is considered an anti-pattern.

Another approach is to use a ServiceFilter. First, create a constructor for the action filter:

public MyActionFilter(string property1)
{
    Property1 = property1;
}

Second, change the action filter to a service filter in the controller:

[ServiceFilter(typeof(MyActionFilter))] 
public ActionResult<string> MyMethod()
{
    ...
}

Third, register the action filter:

builder.Services.AddScoped(p => new MyActionFilter(p.GetService<IConfiguration>().GetValue<string>("myString")));

Here is a great blogpost about the topic: Dependency Injection in action filters in ASP.NET Core.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 kmschaal