从注入配置 class 访问 AppSettings.json 中的顶级字段

Accessing top-level fields in AppSettings.json from injected configuration class

假设我们有这样的 AppSettings.json

{
  "Region": Europe,
  "WeirdService": {
    "JustField": "value"
  }
}

在单独的单例 class 中注册 WeirdService 设置(或使用选项模式)没问题,只是:

service.AddSingleton(configuration.GetSection("WeirdService").Get<WeirdService>();

此时一切正常。但是我不知道如何干净地处理这个顶级属性,例如我的示例中的 Region

我知道我可以直接注入 IConfiguration 并使用 config.GetValue<string>("Region") 或者直接访问配置,但我想知道是否有一些干净、更好的方法而不用在服务中硬编码这些东西。

编辑

忘记说了。我目前合作的团队使用 .NET Core 3.1 作为当前的 LTS 版本。

您也可以将部分绑定到 class,它允许在不使用太多魔术字符串的情况下进行干净的使用。

public class WeirdService{
 public string JustField{ get; set;}  
 public string AnotherField{ get; set;}
 }

然后您可以在控制器中定义一个字段

private readonly WeirdService _weirdService = new WeirdService();
 public UserController(IConfiguration configuration)
{
    configuration.GetSection("WeirdService").Bind(_weirdService);
    
    //_weirdService.JustField
    //_weirdService.AnotherField
}

您可以使用

访问区域字段
configuration.GetValue<string>("Region")

或者其他方式是

public Startup(IConfiguration configuration, IWebHostEnvironment 
 webHostEnvironment)
    {
        Configuration = configuration;
        environment = webHostEnvironment;
    }

那你就可以用

Configuration["Region"]

已更新

方法 1 首选方式

读取相关配置值的首选方法是使用 options pattern.

有关 options pattern 的更多详细信息,请查看下面的 link。

https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?view=aspnetcore-5.0

 "WeirdService": {
     "JustField": "value"
  }

创建 WeiredServiceOptions class.

public class WeiredServiceOptions
{
   public const string WeiredService = "WeiredService";
   public string JustField { get; set; }
}

一个选项class:

  1. 必须是非抽象的 public 无参数构造函数。
  2. 该类型的所有 public 读写属性都已绑定。
  3. 字段未绑定。在前面的代码中,WeiredService 未绑定。使用 Position 属性,因此在将 class 绑定到配置提供程序时,不需要在应用程序中对字符串 WeiredService 进行硬编码。

调用 ConfigurationBinder.BindWeiredServiceOptions class 绑定到 WeiredService 部分。

var weiredServiceOptions = new PositionOptions();
configuration.GetSection(PositionOptions.Position).Bind(positionOptions);

使用选项模式时的另一种方法是绑定 WeiredService 部分并将其添加到 dependency injection service container。在下面的代码中,WeiredServiceOptions是用Configure添加到服务容器中并绑定到配置

services.Configure<WeiredServiceOptions>(Configuration.GetSection(
                                    WeiredServiceOptions.Position));

然后阅读 WeiredService 选项。

private readonly WeiredServiceOptions _options;

public YourClassContructor(IOptions<WeiredServiceOptions> options)
{
   _options = options
}

Console.WriteLine($"JustField: {_options.JustField}");

方法二

service.AddSingleton(configuration.GetSection("WeirdService:JustField").value);

方法三

service.AddSingleton(configuration["WeirdService:JustField"]);

你有两个选择

没有任何顶级字段

所有顶级字段都将进入一个级别。您的配置如下所示:

{
  "App": {
    "Region": "east-us-2",
    "ShowMaintenancePrompt": false
  },
  // other options follow
}

这种方法的优点是您可以随着应用程序的增长不断添加 "App",并继续使用选项模式。

将顶级字段收集到 class 中,并使用 DI

进行注册

对于这样的配置:

{
  "Region": "east-us-2"
}

创建 AppConfig class 如:

internal class AppConfig
{
    public string? Region { get; set; }
}

然后用 DI 注册这个 class:

var toplevelConfig = new AppConfig {
    Region = configuration.GetValue<string>("Region")
};
services.AddSingleton<AppConfig>(toplevelConfig);

您现在可以在任何地方注入 AppConfig

唯一的小缺点是您不能再使用选项模式。

避免直接注入 IConfiguration

我认为最简单的方法是为顶级键创建一个 class。在您的情况下,您可以使用单个 属性 区域创建类似 AppConfig 的内容。然后您只需注册它而不使用 Configuration 对象获取配置部分,Configure 方法无论如何都要求配置接口而不是 ConfigurationSection。

应用程序配置:

public class AppConfig
{
    public string? Region { get; set; }
}

报名人数:

public static IServiceCollection AddOptions(this IServiceCollection services, IConfiguration configuration)
{
    return services.Configure<AppConfig>(configuration);
}

用法:

public class ExampleConsumer
{
    public ExampleConsumer(IOptions<AppConfig> appConfig) {}
}