Search code examples
c#dependency-injectionfactory-patternstrategy-pattern

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


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?


Solution

  • 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"
        }
    }