Null-waarden behouden in configuratie

De .NET-configuratiebinding haalt configuratiewaarden op via configuratieproviders en probeert deze waarden te binden aan objecteigenschappen. Voorheen, toen een configuratiewaarde null was, behandelde de binder deze alsof de waarde helemaal niet bestond en dus de binding overgeslagen. Met andere woorden, er is geen onderscheid gemaakt tussen null waarden en ontbrekende waarden. Dit gedrag heeft aanzienlijke verwarring veroorzaakt voor gebruikers die expliciet gedefinieerde null waarden in hun configuratie hadden verwacht om te worden gerespecteerd en correct gebonden.

Daarnaast heeft de JSON-configuratieprovider eerder waarden in de configuratie geconverteerd null naar lege tekenreeksen. Dit heeft verder bijgedragen aan verwarring, omdat eigenschappen die aan deze waarden zijn gebonden, een lege tekenreeks zouden ontvangen in plaats van de verwachte null.

Met deze wijziging worden beide problemen opgelost. De JSON-configuratieprovider rapporteert null nu waarden correct zonder deze te wijzigen, en de binder behandelt null waarden als geldige invoer, waarbij deze worden gekoppeld zoals elke andere waarde.

De update bevat ook verbeteringen ter ondersteuning van bindingswaarden null binnen matrices en maakt binding van lege matrices mogelijk.

Geïntroduceerde versie

.NET 10

Vorig gedrag

Voorheen, toen een configuratiewaarde was null, behandelde de binder deze alsof de waarde helemaal niet bestond en dus de binding overgeslagen. Het systeem maakte geen onderscheid tussen null waarden en ontbrekende waarden.

Daarnaast heeft de JSON-configuratieprovider null-waarden in de configuratie geconverteerd naar lege tekenreeksen. Dit heeft ertoe geleid dat eigenschappen die aan deze waarden zijn gebonden, een lege tekenreeks ontvangen in plaats van de verwachte nulltekenreeks.

Houd rekening met de volgende inhoud van het configuratiebestand appsettings.json :

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

En de bijbehorende bindingscode:

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)}'")))}");

Uitvoer:

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

Uitleg van de uitvoer:

  • StringProperty: De null waarde in de JSON is door de JSON-provider geconverteerd naar een lege tekenreeks (""), waarbij de oorspronkelijke waarde wordt overschreven.
  • IntProperty: bleef ongewijzigd (123) omdat de provider null heeft geconverteerd naar een lege string, die niet als een int? kon worden geparseerd, waardoor de oorspronkelijke waarde behouden bleef.
  • Array1: Gebonden aan een matrix die twee lege tekenreeksen bevat, omdat elk null matrixelement als een lege tekenreeks is behandeld.
  • Array2: Bleef null omdat een lege matrix [] in de JSON werd genegeerd door de binder.

Nieuw gedrag

Vanaf .NET 10 null zijn waarden nu correct gebonden aan de bijbehorende eigenschappen, inclusief matrixelementen. Zelfs lege matrices worden correct herkend en gebonden als lege matrices in plaats van te worden genegeerd.

Het uitvoeren van hetzelfde codevoorbeeld levert de volgende resultaten op met behulp van de JSON-configuratieprovider:

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

waardetypen die niet null kunnen zijn

De binder bindt null nu ook aan niet-nullable waardetype-eigenschappen (bijvoorbeeld intbool, of een enum) door deze in te stellen op de standaardwaarde van het type (default(T)).

Voorheen leidde het binden van een null aan een dergelijke eigenschap met de JSON-configuratieprovider tot een InvalidOperationException vergelijkbaar met het volgende, omdat de provider de null had geconverteerd naar een lege tekenreeks en een lege tekenreeks niet kan worden geconverteerd naar het doelwaardetype:

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

Vanaf .NET 10 slaagt dezelfde binding en wordt de eigenschap ingesteld op de standaardwaarde. Als bijvoorbeeld een null gebonden is aan een DayOfWeek-eigenschap, resulteert dit in DayOfWeek.Sunday (0), en een null dat gebonden is aan een int-eigenschap resulteert in 0. Er wordt geen uitzondering opgeworpen.

Providers die een echte null-waarde opslaan, zoals de in-memoryprovider, bonden null al aan niet-nullbare waardetype-eigenschappen door de standaardwaarde in te stellen, zelfs al vóór .NET 10, en de configuratiebrongenerator vertoont hetzelfde gedrag. De wijziging maakt de JSON-provider en de op reflectie gebaseerde binder consistent met dat gedrag.

Type van brekende verandering

Dit is een gedragsverandering.

Reden voor wijziging

Het vorige gedrag was verwarrend en leidde vaak tot klachten van gebruikers. Door dit probleem op te lossen, is het configuratiebindingsproces nu intuïtiever en consistenter, waardoor verwarring wordt verminderd en het gedrag wordt afgestemd op gebruikersverwachtingen.

Als u de voorkeur geeft aan het vorige gedrag, kunt u de configuratie dienovereenkomstig aanpassen:

  • Wanneer u de JSON-configuratieprovider gebruikt, vervangt u null-waarden door lege tekenreeksen ("") om het oorspronkelijke gedrag te herstellen, waarbij lege tekenreeksen worden gekoppeld in plaats van null.
  • Voor andere providers die waarden ondersteunen null , verwijdert u de null vermeldingen uit de configuratie om het eerdere gedrag te repliceren, waarbij ontbrekende waarden worden genegeerd en bestaande eigenschapswaarden ongewijzigd blijven.
  • Als u eerder afhankelijk was van een uitzondering wanneer een null waarde was gebonden aan een eigenschap van het niet-null-waardetype, moet u er rekening mee houden dat de binder de eigenschap nu instelt op de standaardwaarde. Als u een ontbrekende waarde of null waarde van een echte waarde wilt onderscheiden, maakt u de eigenschap nullable (bijvoorbeeld int? of DayOfWeek?) en controleert u null na de binding of valideert u de configuratie expliciet.

Betreffende API's