почему IOptions разрешается, даже если он не зарегистрирован - PullRequest
3 голосов
/ 09 марта 2019

В моем проекте .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 разрешается с экземпляром по умолчанию, и я узнаю об ошибке только тогда, когда пытаюсь его использовать (и когда я ожидаю, что значение не будет нулевым).

Могу ли я как-тозаставить его потерпеть неудачу, заявив, что тип экземпляра не зарегистрирован или что-то подобное?Я просто хочу ловить ошибки как можно раньше.

Ответы [ 2 ]

7 голосов
/ 09 марта 2019

Инфраструктура опций настраивается по умолчанию в компоновщике хоста, поэтому вам не нужно AddOptions() самостоятельно. Это, однако, также гарантирует, что вы можете использовать IOptions<T> везде, где хотите, так как фреймворк предоставит вам этот объект опций.

Способ работы опций заключается в том, что фреймворк всегда даст вам T (до тех пор, пока он может его создать) Когда вы настраиваете конфигурацию, используя, например, AddOptions<T> или Configure<T>, в действительности происходит то, что действие конфигурации регистрируется для этого типа T. И когда IOptions<T> будет позднее разрешен, все эти зарегистрированные действия будут выполняться в той последовательности, в которой они зарегистрированы.

Это означает, что для не настроен тип параметров. В этом случае будут использоваться значения по умолчанию для объекта. Конечно, это также означает, что вы не можете определить, действительно ли вы настроили тип параметров и является ли конфигурация действительной. Обычно это нужно делать при использовании значений.

Например, если вам требуется настроить 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();
}

Что бы вы ни делали, имейте в виду, что по умолчанию источники конфигурации настроены для поддержки обновления конфигурации во время выполнения. Таким образом, у вас всегда будет ситуация, когда конфигурация может быть временно недействительной во время выполнения.

1 голос
/ 09 марта 2019

Отличный ответ от 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 читаются лениво.Хотя файл конфигурации может быть прочитан при запуске приложения, требуемый объект конфигурации создается только при первом вызове IOptions.Value.

При сбое десериализации из-за неправильной конфигурации приложения такая ошибка появится только послевызов IOptions.Value.Это может привести к тому, что неправильные конфигурации останутся незамеченными гораздо дольше, чем требуется.С помощью чтения и проверки значений конфигурации при запуске приложения можно предотвратить эту проблему.

Эта статья также поможет вам понять:

Является ли IOptions плохим?

ASP.NET Core 2.2 - проверка параметров

...