Inject Service into Action Filter

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.

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 with Autofac KeyFilterAttibute

You need to make sure that IService and any other dependencies of the action filter are registered as well.

public IServiceProvider ConfigureServices(IServiceCollection services) {
//...

// Autofac
services.AddAutofac();

var builder = new ContainerBuilder();

//core integration
builder.Populate(services);

// Register the components getting filtered with keys
builder.RegisterType<Service1>().Keyed<IService>("test1");
builder.RegisterType<Service2>().Keyed<IService>("test2");

// Attach the filtering behavior to the component with the constructor
builder.RegisterType<CustomActionFilter>().WithAttributeFiltering();

var container = builder.Build();
var serviceProvider = new AutofacServiceProvider(container);

return serviceProvider;
}

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

[ServiceFilter(typeof(CustomActionFilter))]
[Route("api/custom")]
public class CustomController : Controller {
// GET: api/issues
[HttpGet]
[ServiceFilter(typeof(CustomActionFilter))]
public IActionResult Get() {
//...
}
}

Or you could have registered it globally in ConfigureServices like

public void ConfigureServices(IServiceCollection services) {
services.AddMvc(options => {
options.Filters.Add(typeof(CustomActionFilter)); // by type
});

//...
}

Reference Filters : Dependency injection

Reference AutoFac : KeyFilterAttribute Class

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 service into custom ActionFilterAttribute (Web API)?

Almost everything is fine with the your code, but you should register your filter and service in another way.

In Asp Net Core WebAPI there several ways you can register your filter:

  • Globally - for all controllers, actions, and Razor Pages. More information in Microsoft documentation
  • For only one controller/method. More information in Microsoft documentation

Example of global registration:

            services.AddControllers(options => 
{
options.Filters.Add(typeof(LoggerFilterAttribute));
});

Example of method registration in Controller:

I want notice - in this case you should use ServiceFilter - this helps DI resolve any dependecines for your filter.

        [HttpGet]
[ServiceFilter(typeof(LoggerFilterAttribute))]
public IEnumerable<WeatherForecast> Get()
{

}

This is my simple example for this task:

  1. My SimpleService
    public interface ISimpleService 
{
void Notify(string text);
}
public class SimpleService : ISimpleService
{
public void Notify(string text)
{
Console.WriteLine($"Notify from {nameof(SimpleService)}. {text}");
}
}

  1. ActionFilterAttribute
    public class LoggerFilterAttribute : ActionFilterAttribute
{
private readonly ISimpleService _simpleService;

public LoggerFilterAttribute(ISimpleService simpleService)
{
_simpleService = simpleService;
}

public override void OnActionExecuting(ActionExecutingContext context)
{
_simpleService.Notify($"Method {nameof(OnActionExecuting)}");
}

public override Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
_simpleService.Notify($"Method {nameof(OnActionExecutionAsync)}");
return base.OnActionExecutionAsync(context, next);
}
}

  1. The main step - you should choose way of registration, because there is main difference between global registration and per controller/method in code.
  • If you want use this way of registration - you need only register global filter and this is enough. All magic will be do by WebAPI with DI registration.
            services.AddControllers(options => 
{
options.Filters.Add(typeof(LoggerFilterAttribute));
});
  • If you want use registration per controller/method. You need to register your filter in DI. Because without it you will have Exception.
services.AddScoped<LoggerFilterAttribute>();
        [HttpGet]
[ServiceFilter(typeof(LoggerFilterAttribute))]
public IEnumerable<WeatherForecast> Get()
{

}

  1. The last step register my service
services.AddTransient<ISimpleService, SimpleService>();

  1. Results
    Results

Injecting Repository into ActionFilter

You need to add ISqlRepository to your services:

services.AddScoped<ISqlRepository,SqlRepository>();

The reason the sample project referenced did not have to do this is because ILoggerFactory is added via the framework.



Related Topics



Leave a reply



Submit