How can one create and configure multiple instances of the same class with dependency injection?
I have seen similar questions / tutorials but I am not understanding it yet.
- Different implementations of the same interface
- Passing values into constructor along with DI
- Using IServiceProvider
Here is an example:
Container
public class App()
{
public IHost Host { get; }
public App()
{
Host = Microsoft.Extensions.Hosting.Host.
CreateDefaultBuilder().
ConfigureServices((context, services) =>
{
services.AddSingleton<ITemperatureSensor, AcmeTemperatureSensor>();
services.AddSingleton<IAcmeMachine, AcmeMachine>();
services.Configure<TemperatureSensorOptions>(context.Configuration.GetSection(nameof(TemperatureSensorOptions)));
}).
Build();
}
}
Acme Machine
This is where the temperature sensors should be injected.
public class AcmeMachine()
{
public AcmeMachine( Something? )
{
// How inject the temperature sensors?
}
ITemperatureSensor WaterSensor
ITemperatureSensor AirSensor
}
Temperature Sensors
public interface ITemperatureSensor
{
string SerialNumber;
double GetTemperature();
}
public class AcmeTemperatureSensor()
{
public string SerialNumber { get; }
public AcmeTemperatureSensor(IOptions<TemperatureSensorOptions> options)
{
SerialNumber = options.Value.SerialNumber;
}
public double GetTemperature()
{
return 25.0;
}
}
Settings
appsettings.json
{
"WaterSensor": {
"TemperatureSensorOptions": {
"SerialNumber": "123",
},
},
"AirSensor": {
"TemperatureSensorOptions": {
"SerialNumber": "456",
},
}
}
I would suggest to change the appsettings to something like :
Then you will be able to read the config as:
And inject it into some service as
IOptions<Dictionary<string, Sensor>>.Or use a dedicated class instead of
Dictionary:Note that if sensor does not have any other settings the
TemperatureSensorOptionscan be flattened/removed to simplify the config.If you need to construct the
AcmeTemperatureSensorby injecting there these settings then you will need either construct them manually or follow the factory pattern.From the comments:
Personally I would go with factory pattern which can be quite easily be implemented with
Func<>'s (though it can have some downsides too, like adding factory parameters will become not that easy):And then inject the
Func<TemperatureSensorOptions, AcmeTemperatureSensor>as dependency and call it.Also usually in such cases I create and register in DI class like
AcmeTemperatureSensorDepsto encapsulate all theAcmeTemperatureSensordependencies form DI to make it easier to manage them.