Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of mappen te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen om mappen te wijzigen.
Met C# nullable reference types (NRT) kunnen referentietypen worden geannoteerd, waarmee wordt aangegeven of het geldig is dat ze null
kunnen bevatten of niet. Als u nog niet bekend bent met deze functie, is het raadzaam om uzelf ermee vertrouwd te maken door de C#-documentatie te lezen. Nullable referentietypen zijn standaard ingeschakeld in nieuwe projectsjablonen, maar blijven uitgeschakeld in bestaande projecten tenzij er expliciet voor is gekozen.
Op deze pagina wordt de ondersteuning van EF Core voor null-referentietypen geïntroduceerd en worden aanbevolen procedures beschreven voor het werken met deze typen.
Vereiste en optionele eigenschappen
De belangrijkste documentatie over vereiste en optionele eigenschappen en hun interactie met nullable referentietypen is de pagina Vereiste en Optionele eigenschappen . Het is raadzaam om eerst die pagina te lezen.
Opmerking
Wees voorzichtig bij het inschakelen van null-referentietypen voor een bestaand project: eigenschappen van verwijzingstypen die eerder als optioneel zijn geconfigureerd, worden nu geconfigureerd als vereist, tenzij ze expliciet worden geannoteerd om nullable te zijn. Bij het beheren van een relationeel databaseschema kan dit ertoe leiden dat migraties worden gegenereerd waardoor de null-waarde van de databasekolom wordt gewijzigd.
Niet-nullable eigenschappen en initialisatie
Wanneer nullable referentietypen zijn ingeschakeld, geeft de C#-compiler waarschuwingen voor elke niet-geïnitialiseerde niet-nul eigenschap, omdat deze null
zouden bevatten. Als gevolg hiervan kan de volgende algemene manier om entiteitstypen te schrijven niet worden gebruikt:
public class Customer
{
public int Id { get; set; }
// Generates CS8618, uninitialized non-nullable property:
public string Name { get; set; }
}
Als u C# 11 of hoger gebruikt, bieden vereiste leden de perfecte oplossing voor dit probleem:
public required string Name { get; set; }
De compiler garandeert nu dat wanneer uw code een klant instantieert, de eigenschap Name altijd initialiseert. En aangezien de aan de eigenschap toegewezen databasekolom niet-null is, bevatten alle door EF geladen exemplaren ook altijd een niet-nulle Naam.
Als u een oudere versie van C# gebruikt, is constructorbinding een alternatieve techniek om ervoor te zorgen dat uw niet-null-eigenschappen worden geïnitialiseerd:
public class CustomerWithConstructorBinding
{
public int Id { get; set; }
public string Name { get; set; }
public CustomerWithConstructorBinding(string name)
{
Name = name;
}
}
In sommige scenario's is constructorbinding helaas geen optie; navigatie-eigenschappen kunnen bijvoorbeeld niet op deze manier worden geïnitialiseerd. In die gevallen kunt u de eigenschap null
eenvoudig initialiseren met behulp van de operator null-forgiving (maar zie hieronder voor meer informatie):
public Product Product { get; set; } = null!;
Vereiste navigatie-eigenschappen
Vereiste navigatie-eigenschappen vormen een extra moeilijkheid: hoewel een afhankelijke altijd bestaat voor een bepaalde principal, kan het al dan niet worden geladen door een bepaalde query, afhankelijk van de behoeften op dat moment in het programma (zie de verschillende patronen voor het laden van gegevens). Tegelijkertijd kan het onwenselijk zijn om deze eigenschappen nullable te maken, omdat dit ertoe zou leiden dat bij elke toegang deze eigenschappen gecontroleerd moeten worden op null
, zelfs wanneer bekend is dat de navigatie geladen is en dus niet null
kan zijn.
Dit is niet noodzakelijkerwijs een probleem! Zolang een vereiste afhankelijke juist is geladen (bijvoorbeeld via Include
), is het verkrijgen van toegang tot de navigatie-eigenschap gegarandeerd dat deze altijd niet-null retourneert. Aan de andere kant kan de toepassing ervoor kiezen om na te gaan of de relatie is geladen of niet, door te controleren of de navigatie null
is. In dergelijke gevallen is het redelijk om de navigatie als 'nullable' in te stellen. Dit betekent dat vereiste navigatie van de afhankelijke naar de principal:
- Moet niet null-baar zijn als het wordt beschouwd als een programmeurfout voor toegang tot een navigatie wanneer deze niet wordt geladen.
- Moet null kunnen zijn als het voor de toepassingscode acceptabel is de navigatie te controleren om te bepalen of de relatie is geladen of niet.
Als u een strengere benadering wilt, kunt u een niet-nulleerbare eigenschap hebben met een nullable backing-veld:
private Address? _shippingAddress;
public Address ShippingAddress
{
set => _shippingAddress = value;
get => _shippingAddress
?? throw new InvalidOperationException("Uninitialized property: " + nameof(ShippingAddress));
}
Zolang de navigatie correct is geladen, is de afhankelijke toegankelijk via de eigenschap. Als de eigenschap echter wordt geopend zonder eerst de gerelateerde entiteit correct te laden, wordt er een InvalidOperationException
gegenereerd, omdat het API-contract onjuist is gebruikt.
Opmerking
Verzamelingsnavigaties, die verwijzingen naar meerdere gerelateerde entiteiten bevatten, moeten altijd niet-null zijn. Een lege verzameling betekent dat er geen gerelateerde entiteiten bestaan, maar dat de lijst zelf nooit mag zijn null
.
DbContext en DbSet
Met EF is het gebruikelijk om niet-geïnitialiseerde DbSet-eigenschappen te hebben voor contexttypen:
public class MyContext : DbContext
{
public DbSet<Customer> Customers { get; set;}
}
Hoewel dit over het algemeen een compilerwaarschuwing veroorzaakt, onderdrukt EF Core 7.0 en hoger deze waarschuwing, omdat EF deze eigenschappen automatisch initialiseert via weerspiegeling.
In een oudere versie van EF Core kunt u dit probleem als volgt omzeilen:
public class MyContext : DbContext
{
public DbSet<Customer> Customers => Set<Customer>();
}
Een andere strategie is om niet-nullable auto-properties te gebruiken, maar ze te initialiseren met null
en de null-forgiving operator (!) te gebruiken om de compilerwaarschuwing te onderdrukken. De DbContext-basisconstructor zorgt ervoor dat alle DbSet-eigenschappen worden geïnitialiseerd en null wordt nooit op deze eigenschappen waargenomen.
Navigeren en het opnemen van nulbare relaties
Bij het omgaan met optionele relaties is het mogelijk om compilerwaarschuwingen tegen te komen waarbij een werkelijke null
verwijzingsondering onmogelijk zou zijn. Bij het vertalen en uitvoeren van uw LINQ-query's garandeert EF Core dat als er geen optionele gerelateerde entiteit bestaat, er gewoon navigatie naar deze entiteit wordt genegeerd in plaats van te gooien. De compiler is echter niet op de hoogte van deze EF Core-garantie en produceert waarschuwingen alsof de LINQ-query in het geheugen is uitgevoerd, met LINQ naar objecten. Als gevolg hiervan is het noodzakelijk om de operator null-forgiving (!) te gebruiken om de compiler te informeren dat een werkelijke null
waarde niet mogelijk is:
var order = await context.Orders
.Where(o => o.OptionalInfo!.SomeProperty == "foo")
.ToListAsync();
Er treedt een vergelijkbaar probleem op bij het opnemen van meerdere niveaus van relaties in optionele navigatie:
var order = await context.Orders
.Include(o => o.OptionalInfo!)
.ThenInclude(op => op.ExtraAdditionalInfo)
.SingleAsync();
Als u dit veel doet en de betreffende entiteitstypen voornamelijk (of exclusief) worden gebruikt in EF Core-query's, kunt u overwegen om de navigatie-eigenschappen niet nullbaar te maken en deze te configureren als optioneel via de Fluent-API of gegevensaantekeningen. Hiermee worden alle compilerwaarschuwingen verwijderd, waarbij de relatie optioneel blijft; als uw entiteiten echter buiten EF Core worden doorlopen, kunt u null
-waarden waarnemen, hoewel de eigenschappen als niet-nullbare zijn geannoteerd.
Beperkingen in oudere versies
Vóór EF Core 6.0 gelden de volgende beperkingen:
- nl-NL: De openbare API-interface is niet geannoteerd voor null-gevoeligheid (de openbare API was 'null-bewusteloosheid'), waardoor het soms lastig is om te gebruiken wanneer de NRT-functie is ingeschakeld. Dit omvat met name de asynchrone LINQ-operators die worden weergegeven door EF Core, zoals FirstOrDefaultAsync. De openbare API is volledig geannoteerd voor null-functionaliteit vanaf EF Core 6.0.
- Reverse engineering biedt geen ondersteuning voor C# 8 nullable reference types (NRT's): EF Core heeft altijd C#-code gegenereerd die ervan uitging dat de functie is uitgeschakeld. Null-tekstkolommen zijn bijvoorbeeld gegenereerd als een eigenschap met type
string
, nietstring?
, waarbij de Fluent-API of Data-annotaties worden gebruikt om te configureren of een eigenschap vereist is of niet. Als u een oudere versie van EF Core gebruikt, kunt u de gegenereerde code nog steeds bewerken en deze vervangen door C# nullability-annotaties.