asp.net-coredependency-injectionmultiple-instancesasp.net-core-middlewareabstract-factory

Returning multiple implementations of same interface from Abstract Factory with ASP.NET Core


I want to make multiple implementations of a specific interface accessible through an Abstract Factory in .NET Core. Based on a supplied enum the correct implementation should be returned. This is my current code:

public enum ServiceInstanceEnum { SampleE, SampleD }

// MyServiceFactory
public ISampleC CreateService(ServiceInstanceEnum serviceType)
{
    switch (serviceType)
    {
        case ServiceInstanceEnum.SampleE: return new SampleE();
        case ServiceInstanceEnum.SampleD: return new SampleD();
        default: throw new Exception($" The service is not exist...");
    }
}

My service injections on Startup.cs

services.AddScoped<IMyServiceFactory, MyServiceFactory>();
services.AddScoped<ISampleC, SampleD>();
services.AddScoped<ISampleC, SampleE>();

Controller

private readonly ISampleC _sampleC;

public WeatherForecastController(
    ILogger<WeatherForecastController> logger,
    IMyServiceFactory myServiceFactory)
{
    _logger = logger;
    _sampleC = myServiceFactory.CreateService(ServiceInstanceEnum.SampleD);
}

The problem is that my current MyServiceFactory imeplementation creates the services manually, while I want them to be created and managed by the DI Container.


Solution

  • This is solved in .NET 8 with keyed services. The key can be any type. There's no need for a factory

    builder.Services.AddKeyedSingleton<ICache, BigCache>("big");
    builder.Services.AddKeyedSingleton<ICache, SmallCache>("small");
    
    class BigCacheConsumer([FromKeyedServices("big")] ICache cache)
    {
        public object? GetData() => cache.Get("data");
    }
    

    It should be possible to use enums as keys too:

    services.AddKeyedScoped<ISampleC, SampleD>(ServiceInstanceEnum.SampleD);
    services.AddKeyedScoped<ISampleC, SampleE>(ServiceInstanceEnum.SampleE);
    

    and inject it into the controller

    public class WeatherForecastController: Controller
    {
        private readonly ISampleC _sampleC;
    
        public WeatherForecastController(
            ILogger<WeatherForecastController> logger,
            [FromKeyedServices(ServiceInstanceEnum.SampleE)] ISampleC sampleC)
        {
            _logger = logger;
            _sampleC = sampleC;
        }
    

    Or, using primary constructors, just :

    public class WeatherForecastController(
        ILogger<WeatherForecastController> logger,
        [FromKeyedServices(ServiceInstanceEnum.SampleE)] ISampleC sampleC): Controller
    {
        private readonly ISampleC _sampleC=sampleC;
        private readonly ILogger<WeatherForecastController> _logger = logger;
    
        ...
    }