How to Use Dependency Injection in a .Net Core Actionfilterattribute

How can I use Dependency Injection in a .Net Core ActionFilterAttribute?

Instead of resolving at construction, ActionExecutingContext.HttpContext.RequestServices should give you a reference to the request's service container at the time of the request.

So:

public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var svc = filterContext.HttpContext.RequestServices;
var memCache = svc.GetService<IMemoryCache>();
//..etc

How to Inject Dependency in ActionFilterAttribute

It seems the only solution is to get the current DependencyResolver and get the service manually:

var reCaptcha = DependencyResolver.Current.GetService(typeof(IReCaptcha)) as IReCaptcha;

I believe this should work anywhere.

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

If you want to avoid the Service Locator pattern you can use DI by constructor injection with a TypeFilter.

In your controller use

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

And your ActionFilterAttribute does not need to access a service provider instance anymore.

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

For me the annotation [TypeFilter(typeof(MyActionFilterAttribute), Arguments = new object[] {10})]seems to be awkward. In order to get a more readable annotation like [MyActionFilter(Limit = 10)]your filter has to inherit from TypeFilterAttribute. My answer of How do I add a parameter to an action filter in asp.net? shows an example for this approach.

Inject service into Action Filter

Using these articles as reference:

ASP.NET Core Action Filters

Action filters, service filters and type filters in ASP.NET 5 and MVC 6

Using the filter as a ServiceFilter

Because the filter will be used as a ServiceType, it needs to be registered with the framework IoC. If the action filters were used directly, this would not be required.

Startup.cs

public void ConfigureServices(IServiceCollection services) {
services.AddMvc();

services.AddScoped<ISessionService, SessionService>();
services.AddScoped<EnsureUserLoggedIn>();

...
}

Custom filters are added to the MVC controller method and the controller class using the ServiceFilter attribute like so:

[ServiceFilter(typeof(EnsureUserLoggedIn))]
[Route("api/issues")]
public class IssueController : Controller {
// GET: api/issues
[HttpGet]
[ServiceFilter(typeof(EnsureUserLoggedIn))]
public IEnumerable<string> Get(){...}
}

There were other examples of

  • Using the filter as a global filter

  • Using the filter with base controllers

  • Using the filter with an order

Take a look, give them a try and see if that resolves your issue.

Hope this helps.

Inject IConfiguration into ActionFilter

In .NET Core 2.x, IConfiguration is already registered to the DI hence is ready for grab. And normally you would just inject IConfiguration through constructor injection:

public class MyActionFilter : ActionFilterAttribute
{
private readonly IConfiguration _config;

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

That would work but that also means when you use the action filter, you need to supply IConfiguration as one of the parameters:

enter image description here

It would be better if you don't have to provide the dependencies manually.


Use GetService<> instead

One way to go around it is to get the required service(s) on one of the overrides instead:

using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

public class MyActionFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
var config = context.HttpContext.RequestServices.GetService<IConfiguration>();

string recaptchaVersion = config.GetValue<string>("recaptcha:version");

base.OnActionExecuting(context);
}
}

And if your appsettings.json has something like this:

{
"recaptcha": {
"secretKey": "xxx",
"siteKey": "xxx",
"version": "v3"
}
}

Then config.GetValue<> should give you what you want to access

enter image description here

Injecting dependencies into ASP.NET MVC 3 action filters. What's wrong with this approach?

I'm not positive, but I believe you can just use an empty constructor (for the attribute part) and then have a constructor that actually injects the value (for the filter part).*

Edit: After a little reading up, it appears that the accepted way to do this is via property injection:

public class MyActionFilter : ActionFilterAttribute
{
[Injected]
public IMyService MyService {get;set;}

public override void OnActionExecuting(ActionExecutingContext filterContext)
{
MyService.DoSomething();
base.OnActionExecuting(filterContext);
}
}

Regarding the why not use a Service Locator question: It mostly just reduces the flexibility of your dependency injection. For example, what if you were injecting a logging service, and you wanted to automatically give the logging service the name of the class it's being injected into? If you use constructor injection, that would work great. If you're using a Dependency Resolver/Service Locator, you'd be out of luck.

Update

Since this got accepted as the answer, I'd like to go on the record to say that I prefer Mark Seeman's approach because it separates the Action Filter responsibility away from the Attribute. Furthermore, Ninject's MVC3 extension has some very powerful ways to configure action filters via bindings. See the following references for more details:

  • https://github.com/ninject/ninject.web.mvc/wiki/Dependency-injection-for-filters
  • https://github.com/ninject/ninject.web.mvc/wiki/Conditional-bindings-for-filters
  • https://github.com/ninject/ninject.web.mvc/wiki/Filter-configurations

Update 2

As @usr pointed out in the comments below, ActionFilterAttributes are instantiated when the class is loaded, and they last the entire lifetime of the application. If the IMyService interface is not supposed to be a Singleton, then it ends up being a Captive Dependency. If its implementation isn't thread-safe, you could be in for a lot of pain.

Whenever you have a dependency with a shorter lifespan than your class's expected lifespan, it's wise to inject a factory to produce that dependency on-demand, rather than injecting it directly.



Related Topics



Leave a reply



Submit