O acesso síncrono a opções validadas de forma assíncrona gera um erro

A partir do .NET 11 RC 1, o acesso síncrono a um tipo de opções que utiliza apenas validadores assíncronos falha rapidamente. Em vez de devolver uma instância de opções sem validação assíncrona, o processo de criação síncrono lança uma OptionsValidationException.

Versão introduzida

.NET 11 RC 1

Comportamento anterior

Anteriormente, em .NET 11 Preview 6 e Preview 7, IAsyncValidateOptions<TOptions> era independente de IValidateOptions<TOptions>. Os validadores assíncronos eram executados apenas através da via de validação assíncrona no arranque.

Quando acedeu a um tipo de opções com validação assíncrona através de um caminho de criação síncrono, como IOptions<TOptions>.Value, CurrentValue, Get, IOptionsSnapshot<TOptions>.Value, Get ou Create, o validador assíncrono não era executado. O caminho síncrono devolveu uma instância de opções não validada.

Tipos que implementaram IAsyncValidateOptions<TOptions> diretamente só precisavam de implementar ValidateAsync.

Novo comportamento

A partir de .NET 11, RC 1, IAsyncValidateOptions<TOptions> deriva de IValidateOptions<TOptions>, e a interface deixa de ser contravariante. Os validadores assíncronos participam na mesma coleção de validadores que os validadores síncronos.

Quando se acede a um tipo de opções com apenas validadores assíncronos através de um caminho de criação síncrono, o método herdado Validate devolve um ValidateOptionsResult falhado. Create em seguida lança um OptionsValidationException. A mensagem de exceção orienta-te a ligar ValidateOnStart e concluir o arranque antes de aceder sincronizadamente às opções.

Os tipos personalizados que implementam IAsyncValidateOptions<TOptions> diretamente devem agora também implementar o método herdado Validate .

Tipo de mudança disruptiva

Essa alteração é uma mudança comportamental e pode afetar a compatibilidade da fonte. Num cenário restrito em que um binário de pré-visualização implementa diretamente IAsyncValidateOptions<TOptions> sem recompilação, a alteração também pode afetar a compatibilidade binária.

Motivo da mudança

A validação assíncrona de opções foi introduzida no .NET 11 Preview 6 como um caminho de validação apenas para o arranque. Trabalhos posteriores de design para validação pós-arranque expuseram uma lacuna de correção: as opções também têm caminhos síncronos de criação e acesso. Um validador que implementasse apenas a interface assíncrona não conseguia executar esses caminhos síncronos, pelo que opções inválidas podiam ser devolvidas e armazenadas em cache antes da validação assíncrona ser executada.

Para fechar essa lacuna antes de a API atingir uma versão estável, IAsyncValidateOptions<TOptions> deriva agora de IValidateOptions<TOptions>. O contrato unificado mantém uma coleção de validadores, preserva a ordem de registo e faz com que o acesso síncrono não suportado falhe com uma exceção acionável. Para mais informações, consulte dotnet/runtime#131197 e a proposta aprovada da API.

Para opções que utilizam apenas validadores assíncronos, ligue ValidateOnStart e complete o arranque do host antes de aceder às opções de forma síncrona:

services.AddOptions<MyOptions>()
    .Configure(o => o.Value = 42)
    .ValidateAsync(o => Task.FromResult(o.Value > 0), "Value must be positive.")
    .ValidateOnStart();

await host.StartAsync();

Evite o acesso síncrono a opções que tenham apenas validadores assíncronos antes de a inicialização estar concluída. Esta orientação aplica-se a IOptions<TOptions>.Value, IOptionsMonitor<TOptions>.CurrentValue, IOptionsMonitor<TOptions>.Get, IOptionsSnapshot<TOptions>.Value, IOptionsSnapshot<TOptions>.Get, e IOptionsFactory<TOptions>.Create.

Alguns caminhos mantêm-se síncronos mesmo depois de usares ValidateOnStart. A validação na inicialização não propaga valores de IOptionsSnapshot<TOptions> para âmbitos subsequentes, e IOptionsMonitor<TOptions> recria as opções sincronamente após uma alteração da configuração. Se precisares que esses caminhos validem com sucesso, mantém pelo menos um validador síncrono.

Se implementares IAsyncValidateOptions<TOptions> diretamente, adiciona o método herdado Validate(string? name, TOptions options) e recompila com o .NET 11. Retorne ValidateOptionsResult.Skip quando o validador não se aplica, ou retorne ValidateOptionsResult.Fail quando a validação síncrona não for suportada.

Se o seu código dependia da contravariância removida in TOptions , atualize as atribuições, moldes ou registos afetados.

Não é possível controlar este comportamento com um comutador AppContext ou uma definição de configuração.

APIs afetadas