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