Reputation: 2993
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
}
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
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
}
Upvotes: 1