如何使用Configuration GetRequiredSection()读取嵌套配置对象树?
在.NET Core中读取带嵌套属性的配置节问题解决
问题重现
我的配置片段:
{ "SMTP": { "Name": "...", "UserName": "...", "Password": "...", "HostName": "...", "Port": "587", "Security": "STARTTLS", "OAuth2": { "ClientId": "", "ClientSecret": "", "Scopes": [ ] } } }
用以下record类型读取配置:
public record OAuth2Credentials(string ClientId, string ClientSecret, string[] Scopes); public record SmtpConfiguration(string Name, string UserName, string Password, string HostName, int Port, string Security, OAuth2Credentials OAuth2);
执行读取代码时:
SmtpConfiguration smtpConfig = Program.Configuration! .GetRequiredSection("SMTP") .Get<SmtpConfiguration>()!;
抛出错误:
System.InvalidOperationException: Cannot create instance of type 'WebApi.SendEMail.SmtpConfiguration' because parameter 'OAuth2' has no matching config. Each parameter in the constructor that does not have a default value must have a corresponding config entry.
请问如何用Configuration.GetRequiredSection()读取嵌套的对象树?
错误原因
问题出在位置参数式的record构造逻辑:.NET配置绑定器需要严格匹配构造函数的每一个必填参数,而你的OAuth2Credentials要求必须传入Scopes数组,配置里的空数组无法被自动映射到构造参数;同时OAuth2节点内的空字符串值也导致绑定器无法成功构造OAuth2Credentials实例,最终外层的SmtpConfiguration初始化失败。
解决方案
方案1:给Record构造参数添加默认值(最简洁)
修改record定义,给可选参数加上默认值,让绑定器在配置值为空时能使用默认值构造实例:
public record OAuth2Credentials( string ClientId = "", string ClientSecret = "", string[] Scopes = default!); public record SmtpConfiguration( string Name, string UserName, string Password, string HostName, int Port, string Security, OAuth2Credentials OAuth2 = default!);
此时原来的读取代码可以直接正常运行,无需修改。
方案2:使用带属性的Record(替代位置参数)
把record改成属性初始化的形式,让绑定器通过属性赋值而非构造函数填充数据:
public record OAuth2Credentials { public string ClientId { get; init; } = ""; public string ClientSecret { get; init; } = ""; public string[] Scopes { get; init; } = Array.Empty<string>(); } public record SmtpConfiguration { public string Name { get; init; } = ""; public string UserName { get; init; } = ""; public string Password { get; init; } = ""; public string HostName { get; init; } = ""; public int Port { get; init; } public string Security { get; init; } = ""; public OAuth2Credentials OAuth2 { get; init; } = new(); }
这种方式更灵活,即使配置节点为空,也能通过默认值初始化对象。
方案3:你选定的分步绑定方案
你实现的分步绑定确实能保证类型安全,缺点是代码相对繁琐,但可以精准控制每个节点的绑定逻辑:
using System; public record OAuth2Credentials(string ClientId, string ClientSecret) { public string[] Scopes = Array.Empty<string>(); } public record SmtpConfiguration(string Name, string UserName, string Password, string HostName, int Port, string Security) { public OAuth2Credentials OAuth2 = new OAuth2Credentials("", ""); } try { var smtpConfig = Program.Configuration!.GetRequiredSection("SMTP").Get<SmtpConfiguration>()!; smtpConfig.OAuth2 = Program.Configuration!.GetRequiredSection("SMTP:OAuth2").Get<OAuth2Credentials>()!; smtpConfig.OAuth2.Scopes = Program.Configuration!.GetRequiredSection("SMTP:OAuth2:Scopes").Get<string[]>()!; } catch (InvalidOperationException ex) { return TypedResults.BadRequest($"Invalid SMTP configuration: {ex.Message}"); }
适合需要对每个节点绑定做额外校验的场景。
内容的提问来源于stack exchange,提问作者AxD
相关产品推荐
相关产品推荐

