Microsoft DI:如果两个不同的 类 在它们的构造函数参数中采用相同的类型,我如何从配置中为它们注入不同的值?

Microsoft DI: If two different classes take the same type in their constructor params, how can I inject different values for them from configuration?

使用 Microsoft 的通用主机 DI 和配置,假设我想要两个不同的对象(可能是不同的,甚至是不相关的类型),每个对象在其构造函数中都有一个 SomeKindOfParameter。他们可能需要将不同的 SomeKindOfParameter 值注入其中,这些值在配置中指定(例如,在 appsettings.json 中)。我该怎么做?

我知道如何从配置中获取不同 SomeKindOfParameter 实例的不同值:

SomeKindOfParameter parameterForTypeX = config.GetSection("ParamForX").Get<SomeKindOfParameter>();
SomeKindOfParameter parameterForTypeY = config.GetSection("ParamForY").Get<SomeKindOfParameter>();

并且给定一个单个SomeKindOfParameter,我知道如何将它注入到它们中:

SomeKindOfParameter param = ...
services.AddSingleton<SomeKindOfParameter>(param);
services.AddTransient<IX, X>();
services.AddTransient<IY, Y>();

但是如何将 parameterForTypeX 注入 X 并将 parameterForTypeY 注入 Y?谢谢。

“正确”的方法是使用命名选项,如 documentation 中所述。

services.Configure<SomeKindOfParameter>("ParamForX",
                                       Configuration.GetSection("SectionForX"));
services.AddTransient<ServiceX>();

public class ServiceX{
    public ServiceX(IOptionsSnapshot<SomeKindOfParameter> snapshot){
        .... = snapshot.Get("ParamForX");
    }
}

虽然服务集合对于每种类型只有一个服务对象,但您可以通过“开放通用”服务添加一个间接层,以隐藏实现细节并为每个服务提供不同的配置实例;

public class Parameter<T> where T : class {
    public Parameter(IOptionsSnapshot<SomeKindOfParameter> snapshot){
        Value = snapshot.Get(typeof(T).Name);
    }
    public SomeKindOfParameter Value { get; private set; }
}

public ServiceX(Parameter<ServiceX> p){
    .... = p.Value;
}

services.AddSingleton(typeof(Parameter<>));

可能还有一种方法可以动态绑定每个配置部分。基于 .Configureimplemented.

有几种方法:

使用泛型

您可以定义派生自 SomeKindOfParameter:

的通用辅助类型
class SomeKindOfParameter { }

class SomeKindOfParameter<TCategory> : SomeKindOfParameter
{ }

然后相应地为每个服务注入 SomeKindOfParameter of TCategory:

services.AddTransient<SomeKindOfParameter<X>>();
services.AddTransient<IX, X>();

services.AddTransient<SomeKindOfParameter<Y>>();
services.AddTransient<IY, Y>();

class X : IX
{
    public X(SomeKindOfParameter<X> parameters)
    {
    }
}

class Y : IY
{
    public Y(SomeKindOfParameter<Y> parameters)
    {
    }
}

使用ActivatorUtilitiesclass

ActivatorUtilities.CreateInstance 方法允许您将参数直接传递给构造函数:

services.AddTransient<IX>(serviceProvider =>
{
    var config = serviceProvider.GetRequiredService<IConfiguration>();
    SomeKindOfParameter parameterForTypeX = config.GetSection("ParamForX")
        .Get<SomeKindOfParameter>();

    return ActivatorUtilities.CreateInstance<X>(
        serviceProvider,
        // Parameters not passed through this array will be activated
        // from DI container
        new object[]
        {
            parameterForTypeX
        });
});