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<>));
可能还有一种方法可以动态绑定每个配置部分。基于 .Configure
是 implemented.
有几种方法:
使用泛型
您可以定义派生自 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)
{
}
}
使用ActivatorUtilities
class
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
});
});
使用 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<>));
可能还有一种方法可以动态绑定每个配置部分。基于 .Configure
是 implemented.
有几种方法:
使用泛型
您可以定义派生自 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)
{
}
}
使用ActivatorUtilities
class
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
});
});