Skip to content

Named dependency resolution using Microsoft DI. Service locator free. Factory free. Gluten free.

License

Notifications You must be signed in to change notification settings

jernejg/tiny-named-service-resolver

Repository files navigation

tiny-named-service-resolver

Demo

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);

Features

  1. 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];
}))
  1. 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");
  1. 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.

  1. Named services of the same service type need to be registered with the same ServiceLifetime. The following will fail when building IServiceProvider:
// Needs to be transient, or the last two scoped.
services.AddScoped<ISomeService, DefaultService>();
services.AddTransient<ISomeService, FooService>("foo");
services.AddTransient<ISomeService, BarService>("bar");

How it works

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).

About

Named dependency resolution using Microsoft DI. Service locator free. Factory free. Gluten free.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages