Paul Carlton
Paul Carlton

Reputation: 2993

Asp.net 3.0 IServiceProvider cannot inject

sigh This line in startup.cs:

services.AddScoped<IServiceProvider, ServiceProvider>();

Breaks my app, can't debug, just stops after saying that the build was successful. No errors. No exceptions. If I comment out this line, all works again and the app runs.

Why is that happening? The reason I want to inject IServiceProvider is because it is required by ActivatorUtilities and I'm using that in the BaseController to make it easy to validate like such:

public MyController(IServiceProvider serviceProvider) : base(serviceProvider) { }

[HttpPost]
public ActionResult Post(MyViewModel viewModel)
{
    ValidateFor<MyValidator>(viewModel);

    if (!IsValid)
    {
         ... error stuff
    }

    return Ok("Success!");
}

And in the BaseController

protected IserviceProvider ServiceProvider { get; set; }

public BaseController(IServiceProvider serviceProvider)
{
    ServiceProvider = serviceProvider;
}

public void ValidateFor<TValidator>(object instance) where TValidator : IValidator
{
     // injected ServiceProvider here as a protected property of the base controller
     var validator = ActivatorUtilities.CreateInstance<TValidator>(ServiceProvider);

      var result = validator.Validate(instance);

      if (result.IsValid)
      {
            return;
      }

      ... process errors
}

SOLUTION

The solution is to use IServiceProvider in the right way within the context of the controller.

So instead of:

var validator = ActivatorUtilities.CreateInstance<TValidator>(ServiceProvider);

Get rid of the dependency entirely in your controller and use this instead:

var validator = HttpContext.RequestServces.GetService<TValidator>();

Upvotes: 1

Views: 643

Answers (1)

Nkosi
Nkosi

Reputation: 247098

This might be an XY problem.

No need to inject service provider since it can be accessed via the controller's HttpContext for the current request

public BaseController() {

}

public void ValidateFor<TValidator>(object instance) where TValidator : IValidator {
     // Access the service provider via the current request
     var validator = HttpContext.RequestServces.GetService<TValidator>();

      var result = validator.Validate(instance);

      if (result.IsValid) {
            return;
      }

      //... process errors
}

Alternatively the whole service locator approach can be avoided by explicitly injecting the desired service into the action using the [FromServices] attribute

[HttpPost]
public ActionResult Post(MyViewModel viewModel, [FromServices] MyValidator validator) {
    ValidateFor(validator, viewModel);
    if (!IsValid) {
         //... error stuff
    }
    return Ok("Success!");
}

Where the base ValidateFor can be refactored accordignly

protected void ValidateFor<TValidator>(TValidator validator, object instance) 
    where TValidator : IValidator {
    var result = validator.Validate(instance);

    if (result.IsValid) {
        return;
    }

    //... process errors
}

Reference Dependency injection into controllers in ASP.NET Core

Upvotes: 1

Related Questions