You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET Core 7:IServiceCollection的Configure与AddOptions DI行为疑问

问题根源

你移除builder.Services.Configure<MyTestOptions>(myTestConfigurationSection)后拿不到配置值,核心原因是你注册了命名选项,但注入时用的是默认未命名的实例:

  • builder.Services.AddOptions<MyTestOptions>(MyTestOptions.MyTestSectionName) 这行代码注册的是名为"MyTestSection"的命名选项实例
  • 而MyOwnThing里注入的IOptionsMonitor<MyTestOptions>,默认会获取未命名(默认)的选项实例——这个实例在你移除Configure后根本没被注册,所以只能拿到类里定义的默认值String.Empty
正确实现方式

根据你的需求,推荐两种规范的实现方案,选一种即可:


方案1:使用默认(未命名)选项(推荐,适合大多数场景)

直接用AddOptions注册默认实例,不需要指定名称,链式完成绑定、验证、DI注册:

修正后的Program.cs代码:

var myTestConfigurationSection = builder.Configuration.GetSection(MyTestOptions.MyTestSectionName);

// 注册默认选项,完成绑定+数据注解验证+启动时验证
builder.Services.AddOptions<MyTestOptions>()
    .Bind(myTestConfigurationSection)
    .ValidateDataAnnotations()
    .ValidateOnStart();

// 替换你原来手动实例化的代码:从DI获取配置,避免绕过容器管理
builder.Services.AddSingleton<IThing, MyOwnThing>();
builder.Services.AddSomething(sp => 
    sp.GetRequiredService<IOptions<MyTestOptions>>().Value);

var app = builder.Build();

MyOwnThing代码保持不变:

public class MyOwnThing : IThing
{
    private readonly string _property1;

    public MyOwnThing(IOptionsMonitor<MyTestOptions> options)
    {
        _property1 = options.CurrentValue.Property1;
    }

    public async Task DoSomething()
    {
        var x = DoSomethingElse(_property1);
    }

    private object DoSomethingElse(string propValue) 
    {
        // 业务代码
        return null;
    }
}

方案2:使用命名选项(适合多实例配置场景)

如果你确实需要区分不同名称的选项实例,那需要在注入时指定名称:

Program.cs代码:

var myTestConfigurationSection = builder.Configuration.GetSection(MyTestOptions.MyTestSectionName);

// 注册命名选项
builder.Services.AddOptions<MyTestOptions>(MyTestOptions.MyTestSectionName)
    .Bind(myTestConfigurationSection)
    .ValidateDataAnnotations()
    .ValidateOnStart();

// 注册MyOwnThing时,明确注入命名选项
builder.Services.AddSingleton<IThing, MyOwnThing>(sp => 
{
    var optionsMonitor = sp.GetRequiredService<IOptionsMonitor<MyTestOptions>>();
    return new MyOwnThing(optionsMonitor.Get(MyTestOptions.MyTestSectionName));
});

// 给AddSomething传入命名选项的值
builder.Services.AddSomething(sp => 
    sp.GetRequiredService<IOptionsMonitor<MyTestOptions>>()
        .Get(MyTestOptions.MyTestSectionName));

var app = builder.Build();

修正MyOwnThing的构造函数(可选):

如果你想直接在构造函数里拿到命名选项,可以改用IOptionsSnapshot<MyTestOptions>并通过Get方法获取:

public class MyOwnThing : IThing
{
    private readonly string _property1;

    public MyOwnThing(IOptionsSnapshot<MyTestOptions> optionsSnapshot)
    {
        // 获取指定名称的选项实例
        var myTestOptions = optionsSnapshot.Get(MyTestOptions.MyTestSectionName);
        _property1 = myTestOptions.Property1;
    }

    // 其他代码不变
}
关键注意点
  • 不要手动new MyTestOptions()再Bind,这会绕过DI容器的管理,无法享受选项的自动更新、验证等特性
  • ValidateDataAnnotations需要你给MyTestOptions的属性加数据注解才能生效,比如:
    public class MyTestOptions
    {
        public const string MyTestSectionName = "MyTestSection";
    
        [Required(ErrorMessage = "Property1不能为空")]
        public string Property1 { get; set; } = String.Empty;
        public string Property2 { get; set; } = String.Empty;
    }
    
  • ValidateOnStart会在应用启动时就执行验证,提前发现配置错误,而不是等到第一次使用时才抛出异常

内容的提问来源于stack exchange,提问作者Patrice Cote

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 19:42:51