如何使服务注册取决于配置?

时间:2020-06-01 20:15:37

标签: c# .net-core

我想将服务连接到配置文件中,例如

builder.Services.AddTransient<MyService>(s => s.GetServiceFromSetting<MyAppSetting>("MyServiceA"));

我可以改用反射来获取类型(尽管我不确定该配置是否已经在Startup中可用,至少对于Azure函数而言,存在一个问题:https://github.com/Azure/azure-webjobs-sdk/issues/2406)。

我在想,是否有更好的方法可以完成此任务。

2 个答案:

答案 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”是设置值的键。

相关问题