617

I have services that are derived from the same interface.

public interface IService { }
public class ServiceA : IService { }
public class ServiceB : IService { } 
public class ServiceC : IService { }

Typically, other IoC containers like Unity allow you to register concrete implementations by some Key that distinguishes them.

In ASP.NET Core, how do I register these services and resolve them at runtime based on some key?

I don't see any Add Service methods that take a key or name parameter, which would typically be used to distinguish the concrete implementation.

    public void ConfigureServices(IServiceCollection services)
    {            
         // How do I register services of the same interface?            
    }


    public MyController:Controller
    {
       public void DoSomething(string key)
       { 
          // How do I resolve the service by key?
       }
    }

Is the Factory pattern the only option here?

Update1
I have gone though the article here that shows how to use the factory pattern to get service instances when we have multiple concrete implementations. However, it is still not a complete solution. When I call the _serviceProvider.GetService() method, I cannot inject data into the constructor.

For example consider this:

public class ServiceA : IService
{
     private string _efConnectionString;
     ServiceA(string efconnectionString)
     {
       _efConnecttionString = efConnectionString;
     } 
}

public class ServiceB : IService
{    
   private string _mongoConnectionString;
   public ServiceB(string mongoConnectionString)
   {
      _mongoConnectionString = mongoConnectionString;
   }
}

public class ServiceC : IService
{    
    private string _someOtherConnectionString
    public ServiceC(string someOtherConnectionString)
    {
      _someOtherConnectionString = someOtherConnectionString;
    }
}

How can _serviceProvider.GetService() inject the appropriate connection string? In Unity, or any other IoC library, we can do that at type registration. I can use IOption, however, that will require me to inject all settings. I cannot inject a particular connection string into the service.

Also note that I am trying to avoid using other containers (including Unity) because then I have to register everything else (e.g., Controllers) with the new container as well.

Also, using the factory pattern to create service instances is against DIP, as it increases the number of dependencies a client has details here.

So, I think the default DI in ASP.NET Core is missing two things:

  1. The ability to register instances using a key
  2. The ability to inject static data into constructors during registration
6
  • 6
    Possible duplicate of Dependency injection resolving by name Commented Aug 27, 2016 at 5:35
  • Can Update1 be moved to a different question as injecting things in constructors is very different from working out which object to construct Commented Jan 2, 2019 at 11:55
  • Future readers may want to look at my answer here (stackoverflow.com/questions/42402064/…) to avoid..what I would say is .. introducing service-locator into the mix. Just giving another option. Commented Jan 23, 2020 at 13:21
  • 1
    The problem here is the requirement of a key. If we remove the notion of the key, we can have our factory and eat it too. The problem here is business logic we are forcing down into implementation with the standard factory pattern idea (forcing everything to have a key). The volatility is in the business logic, not the implementation. If we consider that as our volatile thing that needs to be abstracted, needing a key goes away. Please check my answer below for implementation details. AMA. Commented Feb 13, 2021 at 23:16
  • 4
    With .NET 8 Microsoft has brought an important update to the extension libraries which brings keyed DI services so this is now supported out of the box. Commented Aug 30, 2023 at 12:15

42 Answers 42

1
2
1

Okay, here a clean and readable answer by using a dictionary

Create a enum with your database Key Name

public enum Database
    {
        Red,
        Blue
    }

In Startup.cs, create a dictionary of function that open a new SqlConnection, then inject the dependency dictionary as Singleton

Dictionary<Database, Func<IDbConnection>> connectionFactory = new()
   {
      { Database.Red, () => new SqlConnection(Configuration.GetConnectionString("RedDatabase")) },
      { Database.Blue, () => new SqlConnection(Configuration.GetConnectionString("BlueDatabase")) }
   };
services.AddSingleton(connectionFactory);

After you can get the instance od the dependency on object constructor like so:

public class ObjectQueries
{
   private readonly IDbConnection _redConnection;
   private readonly IDbConnection _blueConnection;

   public ObjectQueries(Dictionary<Database, Func<IDbConnection>> connectionFactory)
   {
      _redConnection = connectionFactory[Database.Red]();
      _blueConnection = connectionFactory[Database.Blue]();
   }
}

Thanks @Stefan Steiger for the Idea ;)

Sign up to request clarification or add additional context in comments.

Comments

0

While the out of the box implementation doesn't offer it, here's a sample project that allows you to register named instances, and then inject INamedServiceFactory into your code and pull out instances by name. Unlike other facory solutions here, it will allow you to register multiple instances of same implementation but configured differently

https://github.com/macsux/DotNetDINamedInstances

Comments

0

How about a service for services?

If we had an INamedService interface (with .Name property), we could write an IServiceCollection extension for .GetService(string name), where the extension would take that string parameter, and do a .GetServices() on itself, and in each returned instance, find the instance whose INamedService.Name matches the given name.

Like this:

public interface INamedService
{
    string Name { get; }
}

public static T GetService<T>(this IServiceProvider provider, string serviceName)
    where T : INamedService
{
    var candidates = provider.GetServices<T>();
    return candidates.FirstOrDefault(s => s.Name == serviceName);
}

Therefore, your IMyService must implement INamedService, but you'll get the key-based resolution you want, right?

To be fair, having to even have this INamedService interface seems ugly, but if you wanted to go further and make things more elegant, then a [NamedServiceAttribute("A")] on the implementation/class could be found by the code in this extension, and it'd work just as well. To be even more fair, Reflection is slow, so an optimization may be in order, but honestly that's something the DI engine should've been helping with. Speed and simplicity are each grand contributors to TCO.

All in all, there's no need for an explicit factory, because "finding a named service" is such a reusable concept, and factory classes don't scale as a solution. And a Func<> seems fine, but a switch block is so bleh, and again, you'll be writing Funcs as often as you'd be writing Factories. Start simple, reusable, with less code, and if that turns out not to do it for ya, then go complex.

4 Comments

This is called the service locator pattern is and is typically not the best route to go unless you absolutely have to
@JoePhillips Do you have some input as to why its not a good solution? i love the elegance of it. The only downside i can think of is that i creates a instance of all of them everytime you get one.
@Peter The main reason is because it is very very hard to work with. If you are passing in a serviceLocator object into a class, it is not obvious at all what dependencies that class uses since it's getting them all from a magic "god" object. Imagine having to find references of the type you want to change. That ability basically disappears when you're getting everything through a service locator object. Constructor injection is far more clear and reliable
I dunno. The obviousness is not a minus for me... because if I cared about keeping track of how my components leverage their dependencies, I'd have unit tests for that... tests that not only refer to each dependency, but help us understand HOW each dependency is needed. How else are you going to be aware of that, by reading constructors?!?
0

I have run into the same problem and I worked with a simple extension to allow Named services. You can find it here:

It allows you to add as many (named) services as you want like this:

 var serviceCollection = new ServiceCollection();
 serviceCollection.Add(typeof(IMyService), typeof(MyServiceA), "A", ServiceLifetime.Transient);
 serviceCollection.Add(typeof(IMyService), typeof(MyServiceB), "B", ServiceLifetime.Transient);

 var serviceProvider = serviceCollection.BuildServiceProvider();

 var myServiceA = serviceProvider.GetService<IMyService>("A");
 var myServiceB = serviceProvider.GetService<IMyService>("B");

The library also allows you to easy implement a "factory pattern" like this:

    [Test]
    public void FactoryPatternTest()
    {
        var serviceCollection = new ServiceCollection();
        serviceCollection.Add(typeof(IMyService), typeof(MyServiceA), MyEnum.A.GetName(), ServiceLifetime.Transient);
        serviceCollection.Add(typeof(IMyService), typeof(MyServiceB), MyEnum.B.GetName(), ServiceLifetime.Transient);

        serviceCollection.AddTransient<IMyServiceFactoryPatternResolver, MyServiceFactoryPatternResolver>();

        var serviceProvider = serviceCollection.BuildServiceProvider();

        var factoryPatternResolver = serviceProvider.GetService<IMyServiceFactoryPatternResolver>();

        var myServiceA = factoryPatternResolver.Resolve(MyEnum.A);
        Assert.NotNull(myServiceA);
        Assert.IsInstanceOf<MyServiceA>(myServiceA);

        var myServiceB = factoryPatternResolver.Resolve(MyEnum.B);
        Assert.NotNull(myServiceB);
        Assert.IsInstanceOf<MyServiceB>(myServiceB);
    }

    public interface IMyServiceFactoryPatternResolver : IFactoryPatternResolver<IMyService, MyEnum>
    {
    }

    public class MyServiceFactoryPatternResolver : FactoryPatternResolver<IMyService, MyEnum>, IMyServiceFactoryPatternResolver
    {
        public MyServiceFactoryPatternResolver(IServiceProvider serviceProvider)
        : base(serviceProvider)
        {
        }
    }

    public enum MyEnum
    {
        A = 1,
        B = 2
    }

Hope it helps

Comments

0

Most answers either propose a factory method with hard coded instantiation or require all named instances being instantiated in advance. I want a generic solution without instantiating all named services. Here my solution:

Usage:

public MyConsumer(NamedServiceFactory<IMyService> factory) {
  IMyService service = factory.Create("foo");
}

DI registration:

host.ConfigureServices(services => {
  services.AddNamed<IMyService, MyServiceFoo>("foo");
  services.AddNamed<IMyService, MyServiceBar>("bar");
});

Implementation:

namespace Microsoft.Extensions.DependencyInjection
{
    public static class NamedServiceFactoryExtensions
    {
        public static IServiceCollection AddNamed<T>(this IServiceCollection services, string? name, Func<IServiceProvider, T> factory) where T : class
        {
            if (!services.Contains<NamedServiceFactory<T>>())
                services.AddSingleton<NamedServiceFactory<T>>();
            services.AddSingleton<NamedFactory<T>>(sp => new NamedFactory<T>(name, factory));
            return services;
        }

        public static IServiceCollection AddNamed<T, TImpl>(this IServiceCollection services, string? name) where T : class where TImpl : T
            => AddNamed<T>(services, name, sp => sp.GetRequiredService<TImpl>());
    }
}

public class NamedFactory<T>
{
    public string Name { get; private set; }
    private readonly Func<IServiceProvider, T> Factory;

    public NamedFactory(string? name, Func<IServiceProvider, T> factory)
    {
        this.Factory = factory;
        if (string.IsNullOrEmpty(name))
            throw new ArgumentNullException(nameof(name));
        Name = name!;
    }

    public T Create(IServiceProvider serviceProvider) => Factory(serviceProvider);
}

public class NamedServiceFactory<T>
{
    private Dictionary<string, NamedFactory<T>> Factories = new();
    private readonly IServiceProvider ServiceProvider;

    public NamedServiceFactory(IEnumerable<NamedFactory<T>> factories, IServiceProvider serviceProvider)
    {
        this.ServiceProvider = serviceProvider;
        foreach (var factory in factories)
            Factories.Add(factory.Name, factory);
    }

    public T Create(string? name, IServiceProvider? serviceProvider = null)
    {
        if (string.IsNullOrEmpty(name))
            throw new ArgumentNullException(nameof(name));
        if (!Factories.TryGetValue(name!, out var factory))
            throw new InvalidOperationException($"Service not found with name {name}");
        return factory.Create(serviceProvider ?? ServiceProvider);
    }
}

Feel free to add interfaces INamedServiceFactory<T> and INamedFactory<T>.

Comments

0

In my solution instead of named service registration I use attribute on controller action. In this way I make a choice for using one of the implementations of service in DI scope of the action request.

For example, if I have specified service implementation for reports endpoint, my code looks like this:

 public class HomeController : ControllerBase
  {

    private readonly IService _service;

    public HomeController(IService service)
    {
      _service = service;
    }

    [HttpGet]
    [ReportScope]
    public IEnumerable<WeatherForecast> GetReport()
    {
       //Use Service1 implementation, because of ReportScope attribute
      _service.DoSomeThing();
    }
    
    [HttpGet]
    public IEnumerable<WeatherForecast> Get()
    {
       //Use Service2 implementation
      _service.DoSomeThing();
    }

This attribute is processed by custom middleware:

public class ReportScopeLoggingMiddleware
  {
    private readonly RequestDelegate _next;

    public ReportScopeLoggingMiddleware(RequestDelegate next)
    {
      _next = next;            
    }

    public async Task Invoke(HttpContext context, ReportScopeContext scopeContext)
    {        
      var controllerActionDescriptor = context
        .GetEndpoint()
        .Metadata
        .GetMetadata<ControllerActionDescriptor>();

      bool analytical = controllerActionDescriptor.EndpointMetadata.Any(m => m is ReportScopeAttribute);
      if (analytical) scopeContext.SetActive();
      
      await _next(context);
    }       
  }

In this middleware I use ReportScopeContext.

  public class ReportScopeContext
  {
    public bool Active { get; private set; } = false;
    public void SetActive()
    {
      Active = true;
    }
    
  }

This ReportScopeContext has scoped lifetime in DI and I use it to select an implementation of IService:

  services.AddScoped<ReportScopeContext>();
  services.AddTransient<Service2>();
  services.AddTransient<Service1>();
  services.AddTransient<IService>(sp =>
    sp.GetRequiredService<ReportScopeContext>().Active
      ? sp.GetRequiredService<Service1>()
      : sp.GetRequiredService<Service2>());

Comments

0

You can use reflection to find all the implementations in your assembly then register them. Here I use a WebApplicationBuilder to get an IServicesCollection which I then register the implementations against:

var myInterfaceTypes = typeof(SomeClassInTheAssembly).Assembly.GetTypes()
    .Where(t => typeof(IMyInterface).IsAssignableFrom(t) && !t.IsAbstract).ToList();

foreach (var t in myInterfaceTypes)
{
    builder.Services.AddTransient(typeof(IMyIterface), t);
}

// If we have a NeedsList class which needs a list of all the IMyInterface

builder.Services.AddTransient<INeedsList, NeedsList>(
ipt => new NeedsList(
    ipt.GetServices<IMyIterface>().ToList())
);

1 Comment

I guess this does not address how you inject.. Maybe you can do that in the bottom bit when you instantiate the NeedsList ... Having to inject may point to you trying to 'get around' the interface by detecting the implementation class, which is good to avoid with say a strategy pattern or something.
0

You can use url route instead of enum to resolve service type.

Interface which is typed to a Parent Class where BalanceSheetReportDto : ReportDto

public interface IReportService<T> where T : ReportDto
{
    Task<byte[]> GetFileStream(T reportDto);
}

An abstract class that implements it.

public abstract class ReportService : IReportService<ReportDto>
    {
        public abstract Task<byte[]> GetFileStream(ReportDto reportDto);

    }

This abstract class is what we need to resolve concrete types as you will not be able to specify resolver type as IReportService<ReportDto> and return implementation BalaceSheetReportService. Look at next code block.

Service Resolver for DI.

public delegate ReportService ServiceResolver(ReportType key);
    public static IServiceCollection AddReportServices(this IServiceCollection services)
    {
        services.AddScoped<BalanceSheetReportService>();
        
        services.AddScoped<ServiceResolver>(serviceProvider => key =>  
        {  
            switch (key)  
            {  
                case ReportType.BalanceSheet:  
                    return serviceProvider.GetService<BalanceSheetReportService>();
                default:
                    throw new KeyNotFoundException();  
            }  
        });  

And in controller add resolver but not need to cast to specific type.

public class FinancialReportsController : BaseController
    {
        private ServiceCollectionExtension.ServiceResolver _resolver;
    ...
    [HttpPost("balance-sheet")]              
        public async Task<byte[]> GetBalanceSheetReport([FromBody] BalanceSheetReportDto request)
        {
            try
            {
                var reportService =  _resolver(ReportType.BalanceSheet); //magic
                var data = await reportService.GetFileStream(request);

Concrete implementation.

public class BalanceSheetReportService: ReportService 
    {
    ...
    public override async Task<byte[]> GetFileStream(ReportDto reportDto)
        {
            return await GetFileStream((BalanceSheetReportDto) reportDto);
        }

        private  async Task<byte[]> GetFileStream(BalanceSheetReportDto reportDto)
        {

Unrelated but you could inject other services (data classes for example) to abstract class.

private MongoDbContext _context;
 public ReportService(MongoDbContext context) {
 _context = context;
}

And then in your subclasses call this constructor and be done with it.

public BalanceSheetReportService(MongoDbContext context) : base(context) {}

Comments

0

We can solve this problem in an somewhat elegant way without anti-pattern and without modifying the existing service class.

Assume class MyService is where multiple instances are desired:

// we keep this class as is
public class MyService
{
   public MyService(/* deps */) {}
}

public class MyService<T> : MyService where T : IServiceKey
{
    public MyService(/* deps */) : base(/* deps */) {}
}


public interface IServiceKey {}
public abstract class A : IServiceKey {}
public abstract class B : IServiceKey {}

Then we can leverage the standard mechanism for Dependency Injection without any service locator pattern.

services
  .AddSingleton<MyService<A>>()
  .AddSingleton<MyService<B>>();

Comments

0

I use ActivatorUtilities.CreateInstance

0. Test
public static void Test() {
    var serviceProvider = new ServiceCollection()
      .AddSingleton<IServiceX, ServiceC>()
      .AddSingleton<ServiceA>()
      .AddSingleton<ServiceB>()
      .AddSingleton<IRepo, RepoA>()
      .AddSingleton<IPhase, PhaseA>()
      .AddSpecificSingleton<ControllerA, ServiceB>()
      .AddSpecificSingleton<ControllerB, ServiceA>()
      .AddSingleton<ControllerC>()
      .BuildServiceProvider()
    ;
    var controllerA = serviceProvider.GetRequiredService<ControllerA>();
    var controllerB = serviceProvider.GetRequiredService<ControllerB>();
    var controllerC = serviceProvider.GetRequiredService<ControllerC>();
    Debug.Assert(controllerA.ServiceX is ServiceB);
    Debug.Assert(controllerB.ServiceX is ServiceA);
    Debug.Assert(controllerC.ServiceX is ServiceC);

    var serviceX1 = (ServiceX)controllerA.ServiceX;
    var serviceX2 = (ServiceX)controllerB.ServiceX;
    Debug.Assert(ReferenceEquals(serviceX1.Repo, serviceX2.Repo));
}
1. Declare controller

ControllerX has dependency IServiceX

public abstract class ControllerX {
    public IServiceX ServiceX { get; }
    public IPhase ThePhase { get; }

    public ControllerX(IServiceX serviceX, IPhase phase) {
        (ServiceX, ThePhase) = (serviceX, phase);
    }
}

public class ControllerA : ControllerX {
    public ControllerA(IServiceX serviceX, IPhase phase) : base(serviceX, phase) { }
}

public class ControllerB : ControllerX {
    public ControllerB(IServiceX serviceX, IPhase phase) : base(serviceX, phase) { }
}
2. ServiceX

ServiceX has dependency IRepo

public interface IServiceX : IGuidId { }

public abstract class ServiceX : TypeAndId, IServiceX {
    public IRepo Repo { get; }

    public ServiceX(IRepo repo) {
        Repo = repo;
    }
}

public class ServiceA : ServiceX {
    public ServiceA(IRepo repo) : base(repo) { }
}

public class ServiceB : ServiceX {
    public ServiceB(IRepo repo) : base(repo) { }
}
3. Repo
public interface IRepo : IGuidId {
}

public class RepoA : TypeAndId, IRepo { }
public class RepoB : TypeAndId, IRepo { }

// phase
public interface IPhase : IGuidId { }

public class PhaseA : TypeAndId, IPhase { }
public class PhaseB : TypeAndId, IPhase { }
4. Service & Repo have a property Id
public interface IGuidId {
    string Id { get; }
}

public abstract class TypeAndId : IGuidId {
    protected TypeAndId() {
        Id = $"{GetType().Name}-{Guid.NewGuid().ToString()[..7]}";
    }
    public string Id { get; }
}
5. Extension method, to inject specific dependency
public static class InjectExtended {
    public static IServiceCollection AddSpecificSingleton<
        TService,
        TDependencyImplement>(
        this IServiceCollection services)
        where TService : class
        where TDependencyImplement : class
    {
        return services.AddSingleton<TService>(sp => {
            var service = sp.GetRequiredService<TDependencyImplement>();
            return (TService)ActivatorUtilities.CreateInstance(sp, typeof(TService), service);
        });
    }
}

Comments

0
services.AddScoped<IService, Service1>()
                .AddScoped<IService, Service2>();

services.AddSingleton<IFactory, Factory>();

In your controller

 public class SomeController : ControllerBase
{
    private readonly IService iService;
    
    public SomeController (IFactory factory, IEnumerable<IService> 
    services)
    {
        iService = factory.create("someKey", services);//User.serviceKey()
    }

    [Route("health-check")]
    [HttpGet]
    public IActionResult get() 
    {
        return Ok("Success");
    }
 }

In your factory class:

public interface IFactory
{
    IService create(string key, IEnumerable<IService> services);
}

public class Factory : IFactory
{
    private static Dictionary<string, Type> _servicesList;
    
    public Factory()
    {
        if (_servicesList == null) 
        {
            _servicesList = new Dictionary<string, Type>();
            _servicesList.Add("key1", typeof(Service1));
            _servicesList.Add("key2", typeof(Service2));
        }
        
    }
    public IService create(string key, IEnumerable<IService> services)
    {
        return services.Where(s => s.GetType() == 
                   _servicesList.GetValueOrDefault(key)) .Single();
    }
}

Example service interface and implementations:

public interface IService{
    void doSomething();
}

public class Service1 : IService{
   public void doSomething(){
      //doing something....
   }
}

public class Service2 : IService{
   public void doSomething(){
      //doing something....
   }

Comments

-1

FooA, FooB and FooC implements IFoo

Services Provider:

services.AddTransient<FooA>(); // Note that there is no interface
services.AddTransient<FooB>();
services.AddTransient<FooC>();

services.AddSingleton<Func<Type, IFoo>>(x => type =>
{
    return (IFoo)x.GetService(type);
});

Destination:

public class Test
{
    private readonly IFoo foo;

    public Test(Func<Type, IFoo> fooFactory)
    {
        foo = fooFactory(typeof(FooA));
    }

    ....

}

If you want to change the FooA to FooAMock for test purposes:

services.AddTransient<FooAMock>();

services.AddSingleton<Func<Type, IFoo>>(x => type =>
{
    if(type.Equals(typeof(FooA))
        return (IFoo)x.GetService(typeof(FooAMock));
    return null;
});

2 Comments

Sorry Lukasz, this is pretty much the solution that Miguel added at the top, but is also considered an anti-pattern. :( I thought ut was generally a good idea too, until I thought about it.
The reason it's an anti-pattern is that there's a DIFFERENCE between DI and IoC... Effectively the above DOES achieve DI but the issue is that the destination IS DETERMINING AT RUNTIME, what it wants to consume and that's what violates the IoC principle.
1
2

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.