I have a service consuming a set of services implementing an interface IX
:
public class MyService()
{
MyService(IEnumerable<IX> xs)
{
// Store xs in some field and later use them repeatedly.
}
}
I want a number of instances of a class MyX
implementing IX
:
public class MyX : IX
{
public string Field { get; }
public MyX(string field)
{
Field = field;
}
}
I add a number of these either as singletons:
builder.Services.AddSingleton<IX>(new MyX("field value 1"));
builder.Services.AddSingleton<IX>(new MyX("field value 2"));
[UPDATE] … or from configuration:
builder.Services.AddSingleton(configuration.GetSection("xs").Get<IEnumerable<MyX>>());
This implementation works as expected: My service now has an IEnumerable comprising the two distinct instances of MyX
.
However, I need to add a logger to the MyX
class. I try this:
public class MyX : IX
{
ILogger<MyX> _logger;
public string Field { get; }
public X(ILogger<MyX> logger, string field)
{
_logger = logger;
Field = field;
}
}
But now I cannot construct MyX
during service setup, because I do not yet have a logger:
builder.Services.AddSingleton<IX>(new MyX(/* But where would I get a logger? */, "field value 1"));
I’ve run into variants of this problem a number of times. In general, it feels like DI wants me to separate my classes into some form of multi-stage construction (first field, then later, at service resolution time, add the logger). That would entail separating MyX
into a MyXFactory
and a MyX
, but that seems a bit awkward.
What’s the right way to construct some number of instances of MyX
for use with dependency injection?
2
Answers
You can use an override of
AddSingleton<T>
method which accepts a lambda builder where you can access theServiceProvider
.So you can utilize it like below :
Alternatively, you can build a singleton factory
IXFactory
that instantiates and returns the relatedX
implementations.As the question evolves :
Assuming you have access to the
IConfiguration
provided. You can have a configuration class something like below:You can read your config into
XConfig
instance :You may need a using
using Microsoft.Extensions.Configuration;
for this.Now you can use basic foreach to add a singleton instance for each
FieldValue
An alternate approach with a Factory approach and with the possibility of
FieldValues
changing (to give you an idea of edge cases, you can of course just useIOptions<T>
to initiate the instances once)And register this factory as a singleton :
Another option is to use
ActivatorUtilities.CreateInstance
, which allows the resolution of parameters not registered with the DI container:This will allow
MyX
to be instantiated with any number of dependencies, as long as there is only one of typestring
.