Search code examples
c#asp.net-coredependency-injectioninversion-of-control

Register different objects corresponding to an interface in Asp.Net DependencyInjection


I have a use case where I want to register a service multiple times with different configs. The registration of this service is done in a builder extension method. Please check the below code for context.

  1. If I add SomeService as a Singleton then it's not working because the ServiceCollection is not resolving the correct instance for me.
  2. In Autofac, I can use NamedService or KeyedService but that option is not available here. Is there a way to effectively solve this problem?
// a builder extension method
public static IServiceCollection AddSomeService(
    this IServiceCollection services, string myConfig)
{
    services.AddSingleton<TokenCredential>(fa =>
    {
        // a complex logic to register some singleton services which would be
        // useful in below service registration
    });
    services.AddSingleton(fac => // Here I think, AddSingleton is not correct
    {
        ServiceCollection serviceCollection = new ServiceCollection();

        // some complex logic using serviceCollection and myConfig goes here
        ISomeService someService =
            serviceCollection.BuildServiceProvider().GetSomeService();
        return someService;
    });

    return services;
}

In Startup.cs:

public void ConfigureServices(IServiceCollection services)
{
    services.AddSomeService("dbConfig");
    services.AddSingleton<DbRepo, IDbRepo>();

    // I want to register SomeService again with different config and pass it
    // to register CacheRepo
    services.AddSomeService("cacheConfig"); 
    services.AddSingleton<CacheRepo, ICacheRepo>();
}

public class DbRepo : IDbRepo
{
    private readonly ISomeService someService;
    public DbRepo (ISomeService someService)
    {
        this.someService = someService;
    }
}

public class CacheRepo : ICacheRepo
{
    private readonly ISomeService someService;
    public CacheRepo (ISomeService someService)
    {
        this.someService = someService;
    }
}




Solution

  • I would suggest using a factory class, e.g.:

    public interface ISomeServiceFactory
    {
        ISomeService Create(string config);
    }
    
    public sealed class SomeServiceFactory : ISomeServiceFactory
    {
        private readonly IServiceProvider provider;
    
        public SomeServiceFactory(IServiceProvider provider)
        {
            this.provider = provider;
        }
    
        public ISomeService Create(string config)
        {
            // do your processing here instead - you have access to the service provider
        }
    }
    

    Then you can add that to the IServiceCollection:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddSingleton<ISomeServiceFactory>(provider => new SomeServiceFactory(provider));
    }
    

    And inject that into your repos instead:

    public class DbRepo : IDbRepo
    {
        private readonly ISomeService someService;
        public DbRepo(ISomeServiceFactory factory)
        {
            this.someService = factory.Create("dbConfig");
        }
    }
    
    public class CacheRepo : ICacheRepo
    {
        private readonly ISomeService someService;
        public CacheRepo(ISomeServiceFactory factory)
        {
            this.someService = factory.Create("cacheConfig");
        }
    }