I have services that are derived from the same interface.
public interface IService { }
public class ServiceA : IService { }
public class ServiceB : IService
I have created a library for this that implements some nice features. Code can be found on GitHub: https://github.com/dazinator/Dazinator.Extensions.DependencyInjection NuGet: https://www.nuget.org/packages/Dazinator.Extensions.DependencyInjection/
Usage is straightforward:
var services = new ServiceCollection();
services.AddNamed(names =>
{
names.AddSingleton("A"); // will resolve to a singleton instance of AnimalService
names.AddSingleton("B"); // will resolve to a singleton instance of BearService (which derives from AnimalService)
names.AddSingleton("C", new BearService()); will resolve to singleton instance provided yourself.
names.AddSingleton("D", new DisposableTigerService(), registrationOwnsInstance = true); // will resolve to singleton instance provided yourself, but will be disposed for you (if it implements IDisposable) when this registry is disposed (also a singleton).
names.AddTransient("E"); // new AnimalService() every time..
names.AddTransient("F"); // new LionService() every time..
names.AddScoped("G"); // scoped AnimalService
names.AddScoped("H"); scoped DisposableTigerService and as it implements IDisposable, will be disposed of when scope is disposed of.
});
In the example above, notice that for each named registration, you are also specifying the lifetime or Singleton, Scoped, or Transient.
You can resolve services in one of two ways, depending on if you are comfortable with having your services take a dependency on this package of not:
public MyController(Func namedServices)
{
AnimalService serviceA = namedServices("A");
AnimalService serviceB = namedServices("B"); // BearService derives from AnimalService
}
or
public MyController(NamedServiceResolver namedServices)
{
AnimalService serviceA = namedServices["A"];
AnimalService serviceB = namedServices["B"]; // instance of BearService returned derives from AnimalService
}
I have specifically designed this library to work well with Microsoft.Extensions.DependencyInjection - for example:
When you register named services, any types that you register can have constructors with parameters - they will be satisfied via DI, in the same way that AddTransient<>, AddScoped<> and AddSingleton<> methods work ordinarily.
For transient and scoped named services, the registry builds an ObjectFactory so that it can activate new instances of the type very quickly when needed. This is much faster than other approaches and is in line with how Microsoft.Extensions.DependencyInjection does things.