How to dynamically choose what concrete class to use in dependency injection

101 Views Asked by At

I usually write something like this for my DI:

var serviceProvider = new ServiceCollection()
            .AddSingleton<ISomething, Something>()
            .AddSingleton<IOutputMaker, XMLOutputMaker>()
            .AddSingleton<IConfiguration>(Program.configuration)
            .BuildServiceProvider();

But now let's say I read from a config file what type of output I should generate. Notice that above I have this line:

.AddSingleton<IOutputMaker, XMLOutputMaker>()

But I want to be more flexible and say for example if config file says XML then that, if config file says XLSX then maybe this:

.AddSingleton<IOutputMaker, ExcelOutputMaker>()

How can I do that to be more flexible?

I don't know how. Maybe I can call that BuildServiceProvider multiple times?

1

There are 1 best solutions below

0
ProgrammingLlama On BEST ANSWER

You can register the concrete implementations, and then register a factory that consults something from the configuration (I've used options, but you could also just resolve IConfiguration) to determine which one to return.

services.AddOptions<SomeOptions>().BindConfiguration("something");
services.AddSingleton<SomethingImplementationOne>();
services.AddSingleton<SomethingImplementationTwo>();
services
    .AddSingleton<ISomething>(sp => {
        var opts = sp.GetRequiredService<IOptions<SomeOptions>>();
        switch (opts.DefaultService)
        {
            case "One":
                return sp.GetRequiredService<SomethingImplementationOne>();
            default:
                return sp.GetRequiredService<SomethingImplementationTwo>();
        }
    });

For completeness, my SomeOptions class looks like this:

public class SomeOptions
{
    public string DefaultService { get; set; }
}

And my JSON for my configuration looks like this:

{
    "something": {
        "defaultService": "One"
    }
}