为什么注册任意强类型配置后,未注册配置也可通过ServiceProvider获取?
现象底层原因
- 调用任意
services.Configure<T>()方法时,除了注册当前T对应的配置绑定规则,还会一次性注册Options模式的所有通用开放泛型服务:包括IOptionsMonitor<>、IOptions<>、IOptionsFactory<>、IOptionsMonitorCache<>的开放泛型实现。这些服务是全局通用的,只要注册过一次,所有类型参数的对应Options接口都可以被容器构造。 - 当你没有调用过任何
Configure方法时,容器里没有上述开放泛型服务的注册,因此调用GetRequiredService<IOptionsMonitor<ElasticSearchConfiguration>>()会抛出找不到服务的异常,符合预期。 - 当你调用过任意一个
Configure方法后,通用服务已经完成注册,容器可以构造任意T对应的IOptionsMonitor<T>实例。而Options的默认工厂在构造T实例时,即使没有找到任何针对该T的配置规则,也会调用T的无参构造函数生成默认实例(属性均为默认值),不会抛出异常,这就是你拿到非空空属性对象的原因。
存在的使用问题
你当前的测试逻辑存在缺陷:仅通过GetRequiredService是否抛异常来判断配置是否注册,无法覆盖已经注册过任意其他Options的场景。GetRequiredService仅校验对应服务是否可以被容器构造,而不校验该配置类是否有对应的配置绑定规则。
风险提示与解决方案
- 这类缺失注册的情况需要重视:空的默认配置实例会导致后续业务逻辑读取配置时拿到空值,产生不可预期的错误。
- 正确的校验方式可以二选一:
- 直接校验配置的预期值是否符合要求,比如你的示例中
Assert.Equal("elasticsearchserverurl", configurationSection.CurrentValue.ServerUrl)本身就可以校验配置是否正确绑定,空默认实例会直接触发断言失败,不需要依赖GetRequiredService抛异常。 - 如果需要专门校验配置类是否完成注册,可以校验容器中是否存在
IConfigureOptions<你的配置类类型>的服务注册,这个服务是每个配置类调用Configure时单独注册的,没有注册就会返回空。
- 直接校验配置的预期值是否符合要求,比如你的示例中
内容的提问来源于stack exchange,提问作者Daniele Armanasco
相关产品推荐
相关产品推荐

