1

I have a FilterAttribute that has two parameters, one defined in dependency injection and one defined on method of controller as as string

public controller : ControllerBase 
{
    [MyFilter("Parameter1", FromDependency)]
    public ActionResult MyMethod()
    {
         ....
    }
}

and the filter

public MyFilter : Attribute
{
    MyFilter(string parameter1, context fromDependency)
    {
    }
}

How can I inject the parameter from dependency injection?

2
  • Hi, can you transform your attribute to an ActionFilterAttribute? If yes I'll post an answer that explains how to do. Commented Jul 21, 2022 at 8:05
  • Attributes are a compile time thing*, they add metadata to your program, in this case they add metadata to your method, dependency injection is a runtime thing, your attribute can't reach forward in time to determine something that becomes apparent at runtime while you're compiling the code, you could go the other way, add your attribute at runtime, but I don't know how well ASP.NET deals with that Commented Jul 21, 2022 at 8:07

4 Answers 4

5

You can implement an IFilterFactory for this purpose. The runtime checks for this interface when creating filters and calls the CreateInstance method that gets an IServiceProvider as a parameter. You can use this provider to create services and inject them into the filter.

The following sample is taken from the docs:

public class ResponseHeaderFilterFactory : Attribute, IFilterFactory
{
    public bool IsReusable => false;

    public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) =>
        new InternalResponseHeaderFilter();

    private class InternalResponseHeaderFilter : IActionFilter
    {
        public void OnActionExecuting(ActionExecutingContext context) =>
            context.HttpContext.Response.Headers.Add(
                nameof(OnActionExecuting), nameof(InternalResponseHeaderFilter));

        public void OnActionExecuted(ActionExecutedContext context) { }
    }
}

If you need to both use services from DI and values defined on the attribute, you can use the following approach:

public class ResponseHeaderFilterFactory : Attribute, IFilterFactory
{
    private readonly string _attrParam;

    public ResponseHeaderFilterFactory(string attrParam)
    {
      _attrParam = attrParam;
    }

    public bool IsReusable => false;

    public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) 
    {
        var svc = serviceProvider.GetRequiredService<IMyService>();
        return new InternalResponseHeaderFilter(_attrParam, svc);
    }

    private class InternalResponseHeaderFilter : IActionFilter
    {
        private readonly string _attrParam;
        private readonly IMyService _service;

        public InternalResponseHeaderFilter(string attrParam, IMyService service)
        {
          _attrParam = attrParam;
          _service = service;
        }

        public void OnActionExecuting(ActionExecutingContext context) =>
            context.HttpContext.Response.Headers.Add(
                nameof(OnActionExecuting), nameof(InternalResponseHeaderFilter));

        public void OnActionExecuted(ActionExecutedContext context) { }
    }
}

You can then apply the filter like this:

public controller : ControllerBase 
{
    [ResponseHeaderFilterFactory("Parameter1")]
    public ActionResult MyMethod()
    {
         ....
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

Thank you Markus. How can I pass Parameter1? Your solution is usefull if I have only dependency injection parameters
@user3401335 in order to do so, just combine the two approaches: add a constructor parameter to the attribute and use the service provider to inject the service. See the sample.
1

You can implement ActionFilterAttribute to get DI dependencies from HttpContext.RequestServices:

public sealed class MyAttr : ActionFilterAttribute
{
    MyAttr(string parameter1)
    {
    }

    public override void OnActionExecuting(ActionExecutingContext context)
    {
        //Get dependency from HttpContext services
        var myDependency = context.HttpContext.RequestServices.GetService<MyDependency>();

        //Use it
        myDependency.DoSomething();

        //....
    }
}

1 Comment

This will work but it is not recommended. it can be much harder to work out your dependencies with the service locator (anti)pattern.
0

Injecting components into action filter attributes directly is not possible but there are various workarounds to allow us to effectively accomplish the same thing. Using ServiceFilter is a relatively clean way to allow dependency injection into individual action filters.

The ServiceFilter attribute can be used at the action or controller level. Usage is very straightforward:

[ServiceFilter(typeof(MyFilter))]

And our filter:

public class MyFilter: IActionFilter
 { 
    MyFilter(string parameter1, context fromDependency)
    {
    }
 }

Obviously, as we are resolving our filter from the IoC container, we need to register it:

public void ConfigureServices(IServiceCollection services)
{
    ...
    services.AddScoped<MyFilter>(x => 
    new Service(x.GetRequiredService<IOtherService>(),
            "parameter1"));
    ...
}

more details in Paul Hiles article: here

Comments

0

Just to add another option (when using later .NetCore). You can use the IAsyncActionFilter, (there are other filter interfaces that also are available and can use same/similar patern)

So lets say we want to maybe Audit a controller in an API, we can build out the filter to also have pre and post execution like in earlier syntaxes so:

public AuditChangeFilter : IAsyncActionFilter
{
   // DI things in constructor
   public AuditChangeFilter(ILogger<AuditChangeFilter> logger)

   public async Task OnActionExecutionAsync(ActionExecutingContext actionExecutedContext, ActionExecutionDelegate next)
   {        
    // Code to execute before the action executes
    await OnActionExecutingAsync(actionExecutedContext);

    // Call the next delegate/middleware in the pipeline
    var resultContext = await next();

    // Code to execute after the action executes
    await OnActionExecutedAsync(actionExecutedContext, resultContext);
   }

  private async Task OnActionExecutingAsync(ActionExecutingContext actionExecutedContext)
  {
    // Pre execution code
  }

  private async Task OnActionExecutedAsync(ActionExecutingContext actionExecutedContext, ActionExecutedContext resultContext)
  {
    // Post execution code
  }
}

Now in order for this to work we need a little magic in the form of a wrapper filter attribute which will be the decorator:

public class AuditChangeAttribute : TypeFilterAttribute<AuditChangeFilter>
{
  public AuditChangeAttribute()
  {
  }
}

And thats it, oh a part from decorating your API controller method:

[HttpPost]
[ProducesResponseType(typeof(string), 200)]
[AuditChange] // or [AuditChangeAttribute]
public async Task<IActionResult> Post(YourViewModel viewModel)
{
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.