using var host = Host.CreateDefaultBuilder(args)
.UseServiceProviderFactory(new TinyNamedServiceProviderFactory(provider => "foo"))
.ConfigureServices(services =>
{
services.AddTransient<ISomeService, DefaultService>();
services.AddTransient<ISomeService, FooService>("foo");
services.AddTransient<ISomeService, BarService>("bar");
})
.Build();
Debug.Assert(host.Services.GetRequiredService<ISomeService>() is FooService);
- Provide the name used for resolving using a custom
Func<IServiceProvider, string>
:
// just an example of how to select the implementation type
// based on a request header value
.UseServiceProviderFactory(new TinyNamedServiceProviderFactory(provider =>
{
return provider.GetRequiredService<IHttpContextAccessor>()
.HttpContext.Request.Headers["header_name"][0];
}))
- Each named service needs to be registered together with the default implementation, otherwise you won't be able to create the
IServiceProvider
:
// Registration of ISomeService without a name,
// represent the default implementation
services.AddTransient<ISomeService, DefaultService>();
services.AddTransient<ISomeService, FooService>("foo");
services.AddTransient<ISomeService, BarService>("bar");
- Arbitrary order of registration:
It doesn't matter in what order you register services. TinyNamedServiceProviderFactory
makes sure the last service descriptor for a type contains the necessary logic.
- Named services of the same service type need to be registered with the same
ServiceLifetime
. The following will fail when buildingIServiceProvider
:
// Needs to be transient, or the last two scoped.
services.AddScoped<ISomeService, DefaultService>();
services.AddTransient<ISomeService, FooService>("foo");
services.AddTransient<ISomeService, BarService>("bar");
Just before IHostBuilder
initializes the IServiceProvider
a slight modification is made to the last NamedServiceDescriptor
for every named service type inside IServiceCollection
. It introduces (or replaces) the Func<IServiceProvider, TService> implementationFactory
with one that knows which implementation type to pick (it keeps the original though, in case it picks itself).