我想将服务连接到配置文件中,例如
builder.Services.AddTransient<MyService>(s => s.GetServiceFromSetting<MyAppSetting>("MyServiceA"));
我可以改用反射来获取类型(尽管我不确定该配置是否已经在Startup中可用,至少对于Azure函数而言,存在一个问题:https://github.com/Azure/azure-webjobs-sdk/issues/2406)。
我在想,是否有更好的方法可以完成此任务。
答案 0 :(得分:2)
前几天我在为此苦苦挣扎,找不到参考。但是事实证明,这仅仅是因为它实际上非常简单。
您只需使用带有Func<IServiceProvider,TTargetService>
的Services.AddXXX方法,该方法将在解决服务时运行。 EG
services.AddScoped<ISomeService>(sp =>
{
var config = sp.GetService<IConfiguration>();
var serviceType = config.GetValue<string>("SomeServiceType");
if (serviceType=="typeA")
{
return sp.GetService<SomeTypeAService>();
}
else if (serviceType=="typeB")
{
return sp.GetService<SomeTypeAService>();
}
else
{
throw new InvalidOperationException($"SomeServiceType {serviceType} unknown.");
}
});
答案 1 :(得分:2)
大概在应用程序运行时您的配置不会更改。您的配置在启动时加载。依赖项在启动时注册。因此,启动是根据配置来决定要使用哪种实现的理想场所。
假设您已经从配置中读取了一些值。您可以使用字符串,布尔值等。如果存在很多可能性,枚举可能是一个不错的选择。
无论您拥有什么价值,都可以从启动中读取。在这种情况下,我将使用布尔值。
if(someFlag)
{
builder.Services.AddTransient<ISomeService, SomeImplementation>();
}
else
{
builder.Services.AddTransient<ISomeService, SomeOtherImplementation>();
}
现在,不再需要在运行时(相对于启动)从配置中读取代码的情况,而是只需做出一次决策。毕竟,如果值是由在运行时不变的配置值确定的,为什么还要在运行时一遍又一遍地检查该值?
您可以使用实际的类型名称作为设置:
var serviceTypeName = Configuration.GetValue<string>("serviceType");
var serviceType = Type.GetType(serviceTypeName);
if (typeof(IMyService).IsAssignableFrom(serviceType))
{
services.AddTransient<IMyService>(provider => (IMyService)provider.GetRequiredService(serviceType));
}
else
{
throw new Exception("The type name specified in settings doesn't implement IMyService.");
}
又可以打包为可重用的扩展包:
public static class RegisterServiceFromConfigurationExtension
{
public static IServiceCollection RegisterTransientFromConfiguration<TService>(
this IServiceCollection serviceCollection,
IConfiguration configuration,
string typeSettingName)
{
var serviceTypeName = configuration.GetValue<string>(typeSettingName);
var serviceType = Type.GetType(serviceTypeName);
if (typeof(IMyService).IsAssignableFrom(serviceType))
{
serviceCollection.AddTransient<IMyService>(provider =>
(IMyService)provider.GetRequiredService(serviceType));
}
else
{
throw new Exception($"The type name {serviceTypeName} does not implement {typeof(TService)}.");
}
return serviceCollection;
}
}
所以现在整个事情归结为
services.RegisterTransientFromConfiguration<ISomeService>(configuration, "settingName");
其中“ settingName”是设置值的键。