为什么即使未注册 IOptions 也会得到解决

why IOptions is getting resolved even if not registered

在我的 .NET Core 项目中,我在 Configure 方法中有以下设置:

public void ConfigureServices(IServiceCollection services)
{
    services
        .AddMvc()
        .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

    //services.AddOptions<UploadConfig>(Configuration.GetSection("UploadConfig"));
}

我还没有注册任何东西IOptions,我正在将它注入控制器

[Route("api/[controller]")]
[ApiController]
public class HelloWorldController : ControllerBase
{
    public HelloWorldController(IOptions<UploadConfig> config)
    {
        var config1 = config.Value.Config1;
    }
 }

IOptions 正在使用默认实例解析,只有当我尝试使用它时(以及当我希望该值不为空时)我才知道错误。

我能否以某种方式让它失败,说明实例类型未注册或类似的东西?我只想尽早发现错误。

选项框架由默认主机构建器设置,作为其设置的一部分,因此您不需要 AddOptions() 自己。然而,这也确保您可以在任何地方使用 IOptions<T>,因为框架将为您提供确切的选项对象。

选项的工作方式是框架总是会给你一个T(只要它能构建一个)。当您使用例如设置配置时AddOptions<T> or Configure<T>,实际发生的是为该类型 T 注册了配置 action。当稍后解决 IOptions<T> 时,所有这些已注册的操作将按它们注册的顺序 运行。

这意味着没有配置选项类型是有效的。在这种情况下,将使用对象的默认值。当然,这也意味着你无法检测自己是否真的配置了options类型,配置是否真的有效。这通常必须在您使用值时完成。

例如,如果你需要配置Config1,你应该明确地寻找它:

public HelloWorldController(IOptions<UploadConfig> config)
{
    if (string.IsNullOrEmpty(config.Value.Config1))
        throw ArgumentException("Config1 is not configured properly");
}

另一种方法是使用 OptionsBuilder.Validate 为类型注册 验证操作 。当您解析选项对象以验证包含的值时,将自动调用它。这样,您就可以在中央位置设置验证:

services.AddOptions<UploadConfig>()
    .Bind(Configuration.GetSection("UploadConfig"))
    .Validate(c => !string.IsNullOrEmpty(c.Config1));

不幸的是,这也意味着您只能在实际 使用 值时检测到这些问题,如果您没有彻底测试您的应用程序,可能会错过这些问题。解决这个问题的一种方法是在应用程序启动时解析选项并在那里验证它们。

例如,您可以将 IOptions<T> 注入到您的初创公司的 Configure 方法中:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, IOptions<UploadConfig> uploadOptions)
{
    // since the options are injected here, they will be constructed and automatically
    // validated if you have configured a validate action

    // …
    app.UseMvc();
}

或者,如果您有多个要验证的选项,并且想要 运行 不适合验证操作的逻辑,您还可以创建一个验证它们的服务:

public class OptionsValidator
{
    private readonly IOptions<UploadConfig> uploadOptions;
    public OptionsValidator(IOptions<UploadConfig> uploadOptions)
    {
        _uploadOptions = uploadOptions;
    }

    public void Validate()
    {
        if (string.IsNullOrEmpty(_uploadOptions.Value.Config1))
            throw Exception("Upload options are not configured properly");
    }
}

然后将其注入您的 Configure:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, OptionsValidator optionsValidator)
{
    // validate options explicitly
    optionsValidator.Validate();

    // …
    app.UseMvc();
}

无论您做什么,还请记住,默认情况下,配置源配置为支持在 运行 时更新配置。所以你总是会遇到配置在运行-时间暂时无效的情况。

来自 poke 的好回答,我只想用这个来完成它,当没有提供配置时,你可以在启动文件中快速失败:

public class MyOptions
{
    public string MyValue { get; set; }
}

public void ConfigureServices(IServiceCollection services)
{
    var options = Configuration.GetSection("MyOptions").Get<MyOptions>();
    if (string.IsNullOrWhiteSpace(options?.MyValue))
    {
        throw new ApplicationException("MyValue is not configured!");
    }
}

IOptions configuration values are read lazily. Although the configuration file might be read upon application startup, the required configuration object is only created when IOptions.Value is called for the first time.

When deserialization fails, because of application misconfiguration, such error will only appear after the call to IOptions.Value. This can cause misconfigurations to keep undetected for much longer than required. By reading -and verifying- configuration values at application startup, this problem can be prevented.

本文还可以帮助您理解:

Is IOptions Bad?

ASP.NET Core 2.2 – Options Validation