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:
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
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, ActionFilterAttribute
s 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
How to Print a Text File on Thermal Printer Using Printdocument
Using Libtiff from C# (To Access Tiled Tiff Images)
Is the Sorting Algorithm Used by .Net's 'Array.Sort()' Method a Stable Algorithm
Ef Core 2.1 Hasconversion on All Properties of Type Datetime
The 'Await' Operator Can Only Be Used Within an Async Lambda Expression
Split String into Smaller Strings by Length Variable
How to Add Property-Level Attribute to the Typedescriptor at Runtime
Convert.Toint32() a String with Commas
Selecting Attribute Values with HTML Agility Pack
Specified Key Is Not a Valid Size for This Algorithm
There Is No Viewdata Item of Type 'Ienumerable<Selectlistitem>' That Has the Key Country
Should Idisposable.Dispose() Be Made Safe to Call Multiple Times
Caching the Result from a [N Async] Factory Method Iff It Doesn't Throw