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

77 Views Asked by At

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.

2

There are 2 best solutions below

0
On BEST ANSWER

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;

    ...
}
0
On

Let your MyServiceFactory depend on IServiceProvider:

public class MyServiceFactory(IServiceProvider container) : IMyServiceFactory
{
    public ISampleC CreateService(ServiceInstanceEnum serviceType)
    {
        switch (serviceType)
        {
            case ServiceInstanceEnum.SampleE:
                return container.GetRequiredInstance<SampleE>();
            case ServiceInstanceEnum.SampleD:
                return container.GetRequiredInstance<SampleD>();
            default: throw new Exception($" The service is not exist...");
        }
    }   
}

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