.NET8 通过自定义类映射appsettings.json 文件某个节点的配置

目的:在Program 初始化时,希望可以读取 appsettings.json 配置文件的某个节点的内容,自动映射到自定义类中,以便其他消费者进行注入使用配置的属性和值;

步骤1:例如 appsettings.json 配置如下:

csharp 复制代码
{ 
"Settings": { 
"ApiUrl": "<https://wms.api.example.com>", 
"Code": "PROG001" 
 } 
}

步骤2:创建一个自定义类,ServiceOptions

csharp 复制代码
     public class ServiceOptions
     { 
      public string ApiUrl { get; set; } 
      public string Code { get; set; }
     }

该类的属性,必须和 appsettings.json 文件中,Settings 节点的属性相同

步骤3在 Program.cs 中注册服务到 DI 容器

csharp 复制代码
    builder.Services.Configure<ServiceOptions>(builder.Configuration.GetSection("Settings"));

最后:在需要使用的地方,进行自动注入;例如,当前在一个Controller 中去使用配置

csharp 复制代码
    public class SomeController : ControllerBase 
    {
        private readonly ServiceOptions _serviceOptions;
     
        public SomeController(ServiceOptions service)
        {
            _serviceOptions = service;
        }
    }

如果在Program.cs 中注册的自定义配置类,但是由于其他一些服务的注册生命周期和自定义配置类的不同(默认scoped),导致服务无法解析,出现注册失败的情况。

解决方法:把消费者类构造函数改为接收 IOptionsMonitor<T>或 IOptions<T>)如下:

csharp 复制代码
    public class SomeController : ControllerBase 
    {
        private readonly ServiceOptions _serviceOptions;
     
        public SomeController(IOptionsMonitor<ServiceOptions> service)
        {
            _serviceOptions = service?.CurrentValue;
            // 如果需要在配置变更时更新内部状态,可订阅:
		       service.OnChange(newOpts => {
          // 更新 _options 或执行必要逻辑(注意并发)
            _serviceOptions = newOpts;
          });
        }
    }

or

csharp 复制代码
    public class SomeController : ControllerBase 
    {
        private readonly ServiceOptions _serviceOptions;
     
        public SomeController(IOptions<ServiceOptions> service)
        {
            _serviceOptions = service.Value;
        }
    }
  1. 无论使用IOptions,或者是 IOptionsMonitor,都是直接使用_serviceOptions.Code 来直接访问相关属性
  2. 使用优先级
    • 如果配置在运行时需要动态更新并且希望服务能感知这些变化,优先使用 IOptionsMonitor<T>。原因:IOptionsMonitor<T> 提供 CurrentValue 并能通过 OnChange 订阅配置变更,适合运行期需要热更新的场景。
    • 如果配置在启动时读取一次即可且不会变化,使用 IOptions 更简单一些。原因:IOptions<T> 在注入时返回一个固定的快照(startup value),适合只读一次的场景。
    • 绝不要用 IOptionsSnapshot 注入到单例(它是 scoped,只能被 scoped/transient 消费)

另外一种使用情况:如果想 在 Program.cs 初始化时,也需要在 Program.cs 中临时使用 appsettings.json 节点的一些属性的值。

步骤一:除了可以通过 builder.Configuration["Settings:ApiUrl"] 这种方式简单获取,还可以继续使用上面的方式,映射到自定义的类中去获取相关的属性和值,

获取方式一:在 Program.cs 中,配置

csharp 复制代码
    ServiceOptions options = builder.Configuration.GetSection("Settings").Get<ServiceOptions>();

通过 options.Code 这样便能获取到对应的配置项的值;

获取方式二:同样在Program.cs 中,配置

csharp 复制代码
     var options = new ServiceOptions();
     builder.Configuration.GetSection("WcsSettings").Bind(wcsOptions);

获取方式一 同样的道理,都是直接通过 options.Code 获取到对应配置的值

最后一种使用情况:如果在 Program.cs 初始化时,同样需要临时使用 appsettings.json 节点的一些属性配置值,同时也需要把自定义的 ServiceOptions 类进行DI的注入,以便其他消费者类进行使用的话,可以这样去做,更加高效;

Program.cs 中,配置一

csharp 复制代码
    builder.Services.AddOptions<ServiceOptions>()
        .Bind(builder.Configuration.GetSection("Settings"))
        .ValidateDataAnnotations(); // 自动验证注解 

     var options = new ServiceOptions();
     builder.Configuration.GetSection("Settings").Bind(options);

使用自动验证注解的方式,自定义类需要配置注解,Demo 示例如下:

如若缺少 ApiKey 将触发 [Required] 验证错误

复制代码
public class AppSettings
{
    [Required(ErrorMessage = "API Key is required")]
    public string ApiKey { get; set; }

    [Range(1, 100, ErrorMessage = "MaxRetries must be between 1 and 100")]
    public int MaxRetries { get; set; }
}

自定义验证

csharp 复制代码
    builder.Services.AddOptions<ServiceOptions>()
        .Bind(builder.Configuration.GetSection("Settings"))
        .ValidateDataAnnotations() // 内置验证支持
        .Validate(options => options.Timeout > 0, "Timeout 必须大于 0"); // 自定义验证 

启动时,立即验证支持

csharp 复制代码
    builder.Services.AddOptions<ServiceOptions>()
        .Bind(builder.Configuration.GetSection("Settings"))
        .ValidateDataAnnotations()
        .ValidateOnStart(); // 应用启动时立即验证配置 

配置二

csharp 复制代码
     builder.Services.Configure<ServiceOptions>(builder.Configuration.GetSection("Settings"));

     var options = new ServiceOptions();
     builder.Configuration.GetSection("Settings").Bind(options);

这样子就实现了,即能在 Program.cs 中临时使用配置的属性和值,也能自动注入到DI容器中,给其他消费者类进行使用。

总结表格

特性 services.Configure<T>() services.AddOptions<T>().Bind()
绑定配置 ✅ 直接绑定 ✅ 通过Bind方法绑定
DI注册 ✅ 注册IOptions<T> ✅ 相同
链式配置 ❌ 不支持链式,只能单独调用 ✅ 支持链式,可接续配置验证、后置处理等
验证支持 需要单独调用验证方法 ✅ 可链式调用验证(如.ValidateDataAnnotations()
启动验证 需要额外步骤(如调用ValidateOnStart ✅ 可链式调用.ValidateOnStart()
配置覆盖/调整 只能通过多次调用Configure(顺序影响结果) ✅ 可通过多个.Configure调用在绑定前后调整配置
推荐场景 简单绑定,无需额外配置 需要验证、复杂初始化或链式配置的场景

功能等价性与底层机制

  • 底层一致性
    两种方式最终都会调用相同的配置绑定逻辑。AddOptions<T>().Bind() 内部实际执行了 Configure<TOptions>(config) 的绑定过程,因此基础绑定功能完全等价
  • DI 注册效果相同
    无论使用哪种方式,都会将 IOptions<T>IOptionsSnapshot<T> 等接口注册到依赖注入容器,支持通过构造函数注入

结论

  • 功能上等价 :两者在绑定配置部分没有区别,因为Bind内部调用了Configure
  • 灵活性AddOptions<T>提供了更流畅的API,便于添加验证和自定义配置,因此对于需要验证或复杂配置的场景,推荐使用AddOptions<T>().Bind()的方式。
  • 简洁性 :如果只是简单绑定,使用Configure更简洁。
相关推荐
时光追逐者7 小时前
C#/.NET/.NET Core技术前沿周刊 | 第 58 期(2025年10.13-10.19)
微软·开源·c#·.net·.netcore
饺子大魔王的男人11 小时前
JSON可视化工具JSONCrack全解析:使用cpolar从本地到云端的协作升级
json
主宰者12 小时前
【C#】.NET Framework 4.8环境下使用Sqlite的问题总结
sqlite·c#·.net
.NET修仙日记13 小时前
.NET WinForms + WPF 综合学习路线:从传统到现代的.NET桌面开发
学习·c#·.net·wpf·.net core·winforms
组合缺一14 小时前
全球首个支持 IETF JSONPath (RFC 9535) 标准的 Java 框架,Snack4-Jsonpath v4.0.0 发布
java·开发语言·json·jsonpath
小草儿79916 小时前
gbase8s之.net8连接8s之mysql模式(windows)demo
windows·mysql·.net
唐青枫16 小时前
C#.NET Configuration 全面解析:从多环境到强类型绑定实战
c#·.net
sakoba16 小时前
MySQL的json处理相关方法
android·学习·mysql·json
专注VB编程开发20年1 天前
B.NET编写不阻塞UI线程的同步延时
ui·.net·vb.net·doevents