Valores nulos preservados na configuração

O fichário de configuração do .NET recupera valores de configuração por meio de provedores de configuração e tenta vincular esses valores às propriedades do objeto. Anteriormente, quando um valor de configuração era nulo, o vinculador tratava-o como se o valor não existisse de todo e, portanto, omitia a ligação. Por outras palavras, não distinguiu entre null valores e valores em falta. Esse comportamento causou confusão significativa para os usuários que esperavam que os valores explicitamente definidos null em sua configuração fossem respeitados e vinculados corretamente.

Além disso, o provedor de configuração JSON converteu null anteriormente valores na configuração em cadeias de caracteres vazias. Isso contribuiu ainda mais para a confusão, pois as propriedades vinculadas a esses valores receberiam uma cadeia de caracteres vazia em vez da nula esperada.

Esta alteração aborda ambas as questões. O fornecedor de configuração JSON agora relata os valores null corretamente sem os alterar, e o encapsulador trata os valores null como entradas válidas, associando-os como qualquer outro valor.

A atualização também inclui melhorias para oferecer suporte a valores de vinculação null dentro de matrizes e permite a vinculação de matrizes vazias.

Versão introduzida

.NET 10

Comportamento anterior

Anteriormente, quando um valor de configuração era null, o binder tratava-o como se o valor não existisse de todo e, portanto, ignorava o mapeamento. O sistema não distinguia entre null valores e valores ausentes.

Além disso, o provedor de configuração JSON converteu null valores na configuração em cadeias de caracteres vazias. Isso fez com que as propriedades vinculadas a esses valores recebessem uma cadeia de caracteres vazia em vez do esperado null.

Considere o seguinte conteúdo do arquivo de appsettings.json configuração:

{
    "NullConfiguration": {
        "StringProperty": null,
        "IntProperty": null,
        "Array1": [null, null],
        "Array2": []
    }
}

E o código de vinculação correspondente:

public class NullConfiguration
{
    public NullConfiguration()
    {
        // Initialize with non-default value to
        // ensure binding overrides these values.
        StringProperty = "Initial Value";
        IntProperty = 123;
    }
    public string? StringProperty { get; set; }
    public int? IntProperty { get; set; }
    public string[]? Array1 { get; set; }
    public string[]? Array2 { get; set; }
}

var configuration = new ConfigurationBuilder()
                    .AddJsonFile("appsettings.json")
                    .Build().GetSection("NullConfiguration");

// Now bind the configuration.
NullConfiguration? result = configuration.Get<NullConfiguration>();

Console.WriteLine($"StringProperty: '{result!.StringProperty}', intProperty: {(result!.IntProperty.HasValue ? result!.IntProperty : "null")}");
Console.WriteLine($"Array1: {(result!.Array1 is null ?
    "null" : string.Join(", ", result!.Array1.Select(a => $"'{(a is null ? "null" : a)}'")))}");
Console.WriteLine($"Array2: {(result!.Array2 is null ?
    "null" : string.Join(", ", result!.Array2.Select(a => $"'{(a is null ? "null" : a)}'")))}");

Output:

StringProperty: '', intProperty: 123
Array1: '', ''
Array2: null

Explicação do resultado:

  • StringProperty: O null valor no JSON foi convertido pelo provedor JSON em uma cadeia de caracteres vazia (""), substituindo o valor inicial.
  • IntProperty: Permaneceu inalterado (123) porque o provedor converteu null em uma cadeia de caracteres vazia, que não pôde ser analisada como um int?, então o valor original foi mantido.
  • Array1: Vinculado a uma matriz contendo duas cadeias de caracteres vazias porque cada null elemento da matriz foi tratado como uma cadeia de caracteres vazia.
  • Array2: Foi mantido null porque uma matriz vazia [] no JSON foi ignorada pelo sistema de ligação.

Novo comportamento

A partir do .NET 10, null os valores agora estão devidamente vinculados às suas propriedades correspondentes, incluindo elementos de matriz. Mesmo matrizes vazias são corretamente reconhecidas e vinculadas como matrizes vazias em vez de serem ignoradas.

A execução do mesmo exemplo de código produz os seguintes resultados usando o provedor de configuração JSON:

StringProperty: 'null', intProperty: null
Array1: 'null', 'null'
Array2:

Tipos de valor não anulável

O associador passa agora também a associar null a propriedades não anuláveis de tipo valor (por exemplo, int, bool ou uma enumeração), definindo-as com o valor predefinido do tipo (default(T)).

Anteriormente, com o provedor de configuração JSON, associar um null a essa propriedade gerava um InvalidOperationException semelhante ao seguinte, porque o provedor tinha convertido o null numa cadeia vazia e uma cadeia vazia não pode ser convertida para o tipo de valor de destino:

Failed to convert configuration value at 'SomeSection:DayOfWeekProperty' to type 'System.DayOfWeek'.

A partir do .NET 10, a mesma ligação tem sucesso e a propriedade é definida para o seu valor predefinido. Por exemplo, um null associado a uma propriedade DayOfWeek resulta em DayOfWeek.Sunday (0), e um null associado a uma propriedade int resulta em 0. Nenhuma exceção é lançada.

Os fornecedores que armazenam um valor realnull, como o fornecedor em memória, já estão vinculados null a propriedades do tipo de valor não anulável ao definir o valor predefinido, mesmo antes do .NET 10, e o gerador de código-fonte de configuração também corresponde a este comportamento. A alteração torna o fornecedor de JSON e o associador baseado na reflexão consistentes com esse comportamento.

Tipo de mudança disruptiva

Trata-se de uma mudança de comportamento.

Motivo da mudança

O comportamento anterior era confuso e frequentemente levava a reclamações dos usuários. Ao resolver esse problema, o processo de vinculação de configuração agora é mais intuitivo e consistente, reduzindo a confusão e alinhando o comportamento com as expectativas do usuário.

Se preferir o comportamento anterior, você pode ajustar sua configuração de acordo:

  • Ao usar o provedor de configuração JSON, substitua null valores por cadeias de caracteres vazias ("") para restaurar o comportamento original, onde as cadeias de caracteres vazias são vinculadas em vez de null.
  • Para outros provedores que oferecem suporte a null valores, remova as null entradas da configuração para replicar o comportamento anterior, onde os valores ausentes são ignorados e os valores de propriedade existentes permanecem inalterados.
  • Se anteriormente confiava numa exceção quando um null valor estava vinculado a uma propriedade do tipo valor não anulável, note que o binder agora define a propriedade para o seu valor padrão. Para distinguir um valor em falta ou null de um valor real, torne a propriedade capaz de admitir nulos (por exemplo, int? ou DayOfWeek?) e verifique null após a associação, ou valide explicitamente a configuração.

APIs afetadas