Assuming this use case:
You've got two classes X and Y that depends on a configuration of type Config
public class X
{
public X(IOptions<Config> config)
{
}
}
public class Y
{
public Y(IOptions<Config> config)
{
}
}
Now, you want to create each an instance of X and Y, but with different configurations. What would be the right way to register this?
From everything I read, the only way to solve this would be by adding some sort of "naming" for the different configuration instances and resolve them via a custom resolver:
public delegate Config ServiceResolver(string key);
services.AddTransient<ServiceResolver>(serviceProvider => key =>
{
switch (key)
{
case "A":
return ... (whatever to get the first config instance);
case "B":
return ... (whatever to get the second config instance);
default:
throw new KeyNotFoundException();
}
});
However, this means that the implementation of each X and Y must know about details about how to get the configurations:
- They must know the correct name (A or B) and
- they must know the ConfigResolver type, which is only an implementation detail/helper class for the sake of dependency injection.
This problem hits even harder if you need to go through several stages of dependencies, like
Config (A) Config (B)
| |
v v
Service Service
| |
v v
X Y
My feeling is, there should be a better way to solve this. Like some form of receipent dependent service factory:
Host.CreateDefaultBuilder(args).ConfigureServices((context, services) => {
services.Configure<Config>(context.Configuration.GetSection("ConfigA")).For<X>();
services.Configure<Config>(context.Configuration.GetSection("ConfigB")).For<Y>();
});
and maybe
Host.CreateDefaultBuilder(args).ConfigureServices((context, services) => {
services.AddTransient<Service>((services, receiverType) => {
if(receiverType == typeof(X)) {
... resolve a service instance;
}
else {
... resolve some other service instance;
}
});
});
So, is there just some feature I missed until now? Is my understanding of the situation totaly misguided? Or is this really a feature that should be, but has not been added until now?
EDIT:
To make my point clearer: Just assume that X and Y are classes of a third-party library. Their constructors signature cannot be changed by you, as you don't have access to the source code.
So, how would you set this up in a way that you can get each an instance of X with ConfigA and an instance of Y with ConfigB?
Another EDIT 2023-01-02:
Happy new year everyone :)
Seems I have to describe a bit better what's my problem. This is not constrained to IOptions/configurations, but more a general question about where to decide about which service to inject and how it is configured.
Assume I have two a congress location with 2 stages. I call them "bigStage" and "smallStage", but in the end they've got the same implementation. I also got two speakers invited, called "loadSpeaker" and "quietSpeaker", but at this moment in time I don't know which one will speak on which of the two stages.
So I decide I've got this setup:
class Stage {
public Stage(string name, ISpeaker speaker) {
...
}
}
class Speaker: ISpeaker {
public Speaker(string name) {
...
}
}
Now, at the latest time possible, I want to compose my final setup so that I've got 2 Stages (called bigStage and smallStage) and their assigned Speakers (loudSpeaker on bigStage and quietSpeaker on smallStage). This composition/assignment should completely happen in my composition root, so that no code changes have to happen in the rest of my code. How can I do that?