Reputation: 431
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?
Upvotes: 42
Views: 46540
Reputation: 83
I'm browsing around for more information, seems like what we need is here:
The following filters support constructor dependencies provided from DI:
1. ServiceFilterAttribute
2. TypeFilterAttribute
3. IFilterFactory implemented on the attribute.
Reference: microsoft doc
Upvotes: 1
Reputation: 335
A good option is doing this (Tested in .NET Core 3.1):
Inside a Filter class put this:
public static class 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}");
}
}
}
Inside the Startup.cs/ConfigureServices(IServiceCollection services) put this:
services.AddControllers().AddMvcOptions(options => options.Filters.Add(new FilterContainer.GenericFilter()));
The result is that a request to any kind of action inside your .NET Core app will go in and out through this pipeline without declaring a filter attribute above any action.
Let me show you an example inside the Output window of Visual Studio:
[action]: JSON STARTING
[action]: JSON FINISHED
[action]: JSON GIVING RESULT
[action]: JSON RESULT GIVEN. Value: TestId: 103, FullName:...
Upvotes: -1
Reputation: 13767
You can use ServiceFilters to instantiate the ActionFilters you need in the controller.
In the controller:
[ServiceFilter(typeof(TrackingAttribute), Order = 2)]
You need to register TrackingAttribute in the dependency container so the ServiceFilter can resolve it.
Read more about this at https://www.strathweb.com/2015/06/action-filters-service-filters-type-filters-asp-net-5-mvc-6/
Upvotes: 7
Reputation: 24083
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?
Upvotes: 42
Reputation: 15415
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.
Upvotes: 40