Closed abgenullt closed 1 year ago
Hi @abgenullt, thank you for reaching out. In v5.0.0
, I've published additional extension methods for IServiceProvider
that allows named resolution in the way you posted.
If you don't want to update to v5.0.0
, you can use an IDependencyResolver
(instead of IServiceProvider
) which has the ability to resolve named services with Resolve<IService>(serviceName)
.
Let me know your thoughts!
Awesome, thank you!
I will test your solution next week and give feedback.
Regards abgenullt
I try to resolve the service with the new version 5.0 and the IServiceProvider but I get a NotSupportedException ('Only a StashboxServiceProvider can serve named resolution requests.'). Is there something I am missing?
Regards abgenullt
Hi @abgenullt, the NotSupportedException
means that you called the GetService(object name)
on a simple ServiceProvider
. However, it is only supported on a StashboxServiceProvider
because the Microsoft.Extensions.DependencyInjection
service provider has no such functionality. The extension replaces the default service provider with StashboxServiceProvider
when you call the ServiceCollection.UseStashbox()
or IHostBuilder.UseStashbox()
extension methods.
May I ask how you integrate Stashbox into your ASP.NET Core application?
Hi, sure. I'm using the default template "ASP.NET Core Web API" with .NET 6. My code looks like this:
Program.cs
using Stashbox;
using TestApi.Test;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Host.UseStashbox();
builder.Host.ConfigureContainer<IStashboxContainer>((context, container) =>
{
// Execute container validation in development mode.
if (context.HostingEnvironment.IsDevelopment())
{
container.Validate();
}
});
builder.Services.AddControllers()
.AddControllersAsServices();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddScoped<IService, ServiceA>("a");
builder.Services.AddScoped<IService, ServiceB>("b");
builder.Services.AddScoped<IService, ServiceC>("c");
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseAuthorization();
app.MapControllers();
app.Run();
This is my controller:
using Microsoft.AspNetCore.Mvc;
using TestApi.Test;
namespace TestApi.Controllers;
[ApiController]
[Route("[controller]")]
public class GreetingsController : Controller
{
private readonly IServiceProvider _serviceProvider;
public GreetingsController(IServiceProvider serviceProvider) => _serviceProvider = serviceProvider;
[HttpGet("[action]")]
public Task<string> SayHello()
{
var service = _serviceProvider.GetService<IService>("b"); // <- Throws exception
return service.Greetings();
}
}
Hi @abgenullt, thanks for the example! I've found the bug and released the fix in v5.1.0
. Let me know if there's any further issues!
Thank you very much, but with the new version, I get an AggregateException at "container.Validate()":
Container validation failed. See the inner exceptions for details. (Expression of type 'System.IServiceProvider' cannot be used for constructor parameter of type 'Microsoft.Extensions.DependencyInjection.IServiceProviderIsService' (Parameter 'arguments[2]')) (Expression of type 'System.IServiceProvider' cannot be used for constructor parameter of type 'Microsoft.Extensions.DependencyInjection.IServiceProviderIsService' (Parameter 'arguments[2]')) (Expression of type 'System.IServiceProvider' cannot be used for constructor parameter of type 'Microsoft.Extensions.DependencyInjection.IServiceProviderIsService' (Parameter 'arguments[2]')) (Expression of type 'System.IServiceProvider' cannot be used for constructor parameter of type 'Microsoft.Extensions.DependencyInjection.IServiceProviderIsService' (Parameter 'arguments[2]'))
And this is one of the inner exceptions:
Expression of type 'System.IServiceProvider' cannot be used for constructor parameter of type 'Microsoft.Extensions.DependencyInjection.IServiceProviderIsService' (Parameter 'arguments[2]')
Thanks for the report! Sorry for missing this. I'm going to work on the fix.
@abgenullt I've released the fix in v5.1.1
, please let me know if you see further issues!
Thank you for your effort. The resolving is working now.
But shouldn't the GetService method return "null", if a service could not be resolved, but not throwing an "ResolutionFailedException"? This should only happen with the GetRequiredService, if I understand the purposes correctly.
Yes, you explained it correctly, that's the difference between GetService()
and GetRequiredService()
. Have you received a ResolutionFailedException
by calling GetService()
? If so, then it's a bug.
I have found the issue, I've put the wrong Stashbox resolution method behind the generic version of GetService<T>()
🤦. Really sorry, the fix is on the way!
I've released a fix in v5.1.2
. My apologies for the inconvenience, let me know if you spot further issues!
No problem, I'm glad to help.
Hi,
I have a service interface with different implementations (registered with different names) like this:
I need to resolve this inside a request of a controller. I would normally use the IServiceProvider for that. But how can I resolve named services?
This is my sample controller:
Regards abgenullt