.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
相关产品推荐
相关产品推荐

