Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Odkazové typy s možnou hodnotou null (NRT) v jazyce C# umožňují anotovat odkazové typy, které naznačují, zda mohou obsahovat null nebo ne. Pokud s touto funkcí začínáte, doporučujeme, abyste se s ní seznámili čtením dokumentace jazyka C#. Odkazové typy s možnou hodnotou null jsou ve výchozím nastavení povoleny v nových šablonách projektů, ale zůstávají v existujících projektech zakázány, pokud se explicitně nezavolí.
Tato stránka představuje podporu typů odkazů s možnou hodnotou null a popisuje osvědčené postupy pro práci s nimi.
Povinné a volitelné vlastnosti
Hlavní dokumentace k požadovaným a volitelným vlastnostem a jejich interakci s nullable referenčními typy je na stránce Povinné a Volitelné vlastnosti. Doporučujeme začít tím, že si nejprve přečtete tuto stránku.
Poznámka:
Při povolování odkazových typů s možnou hodnotou null u existujícího projektu buďte opatrní: vlastnosti typu odkazu, které byly dříve nakonfigurovány jako volitelné, budou nyní nakonfigurovány podle potřeby, pokud nejsou explicitně opatřeny poznámkami, aby byly nullable. Při správě schématu relační databáze může dojít k vygenerování migrací, které změní hodnotu null sloupce databáze.
Nenulové vlastnosti a inicializace
Pokud jsou povoleny odkazové typy s možnou hodnotou null, kompilátor jazyka C# generuje upozornění pro jakoukoli neinicializovanou vlastnost, jelikož by způsobila null. V důsledku toho nelze použít následující běžný způsob psaní typů entit:
public class Customer
{
public int Id { get; set; }
// Generates CS8618, uninitialized non-nullable property:
public string Name { get; set; }
}
Pokud používáte C# 11 nebo novější, požadované členy poskytují dokonalé řešení tohoto problému:
public required string Name { get; set; }
Kompilátor teď zaručuje, že když kód vytvoří instanci zákazníka, vždy inicializuje jeho vlastnost Name. A vzhledem k tomu, že sloupec databáze namapovaný na vlastnost není null, všechny instance načtené systémem EF vždy obsahují název, který není null.
Pokud používáte starší verzi jazyka C#, vazba konstruktoru je alternativní technikou, která zajistí inicializaci nenulových vlastností:
public class CustomerWithConstructorBinding
{
public int Id { get; set; }
public string Name { get; set; }
public CustomerWithConstructorBinding(string name)
{
Name = name;
}
}
V některých scénářích bohužel není možné použít vazbu konstruktoru; například nelze tímto způsobem inicializovat vlastnosti navigace. V těchto případech můžete jednoduše inicializovat vlastnost null pomocí operátoru null-forgiving (další podrobnosti najdete níže):
public Product Product { get; set; } = null!;
Požadované navigační vlastnosti
Požadované navigační vlastnosti představují další potíže: i když závislá entita vždy existuje pro danou hlavní entitu, může nebo nemusí být načtena konkrétním dotazem podle aktuálních potřeb programu (viz různé vzory načítání dat). Současně může být nežádoucí učinit tyto vlastnosti nulovatelnými, protože by to vynutilo provádět kontrolu na null při každém přístupu k nim, i když je známo, že navigace je načtena, a proto nemůže být null.
To nemusí být nutně problém! Pokud je požadovaný závislý správně načten (např. prostřednictvím Include), se zaručuje, že přístup k navigační vlastnosti vždy vrátí hodnotu, která není null. Na druhou stranu se aplikace může rozhodnout zkontrolovat, zda je vztah načten kontrolou, zda je navigace null. V takových případech je vhodné nastavit, aby navigace byla null. To znamená, že požadované navigace ze závislého na hlavní objekt:
- Pokud je považováno za chybu programátora přistupovat k navigaci, když není načtená, mělo by to být nenulové.
- Mělo by mít hodnotu null, pokud je přijatelné, aby kód aplikace kontroloval navigaci a určoval, zda je relace načtena.
Pokud chcete přísnější přístup, můžete mít vlastnost, která nemůže být null, s polem, které může mít hodnotu null jako záložní úložiště.
private Address? _shippingAddress;
public Address ShippingAddress
{
set => _shippingAddress = value;
get => _shippingAddress
?? throw new InvalidOperationException("Uninitialized property: " + nameof(ShippingAddress));
}
Pokud je navigace správně načtena, bude závislý objekt přístupný prostřednictvím vlastnosti. Pokud je však vlastnost přístupná bez toho, aby se nejprve správně načítala související entita, vyvolá se chyba InvalidOperationException , protože kontrakt rozhraní API byl použit nesprávně.
Poznámka:
Navigace v kolekcích, které obsahují odkazy na více souvisejících entit, by měly být vždy nenulovatelné. Prázdná kolekce znamená, že neexistují žádné související entity, ale samotný seznam by nikdy neměl být null.
DbContext a DbSet
U EF je běžné, že vlastnosti DbSet nejsou inicializovány u typů kontextu:
public class MyContext : DbContext
{
public DbSet<Customer> Customers { get; set;}
}
I když to obvykle způsobuje upozornění kompilátoru, EF Core 7.0 a vyšší potlačí toto upozornění, protože EF tyto vlastnosti automaticky inicializuje prostřednictvím reflexe.
Ve starší verzi EF Core můžete tento problém obejít následujícím způsobem:
public class MyContext : DbContext
{
public DbSet<Customer> Customers => Set<Customer>();
}
Další strategií je použít automatické vlastnosti, které nemohou být null, ale inicializovat je na null a pomocí operátoru „null-forgiving“ (!) umlčet upozornění od kompilátoru. Základní konstruktor DbContext zajišťuje, že všechny vlastnosti DbSet budou inicializovány a hodnota null nebude nikdy pozorována na nich.
Navigace a zahrnutí vztahů s nulovými hodnotami
Při práci s volitelnými relacemi je možné narazit na upozornění kompilátoru, kde by skutečná výjimka odkazu nebyla možná. Při překladu a spouštění dotazů LINQ EF Core zaručuje, že pokud neexistuje volitelná související entita, bude se žádná navigace na ni jednoduše ignorovat, a ne vyvolání. Kompilátor však o této zárukě EF Core neví a vygeneruje upozornění, jako by byl dotaz LINQ spuštěný v paměti, s LINQ to Objects. V důsledku toho je nutné pomocí operátoru null-forgiving (!) informovat kompilátor, že skutečná null hodnota není možná:
var order = await context.Orders
.Where(o => o.OptionalInfo!.SomeProperty == "foo")
.ToListAsync();
K podobnému problému dochází v případě, že mezi volitelnými navigacemi zahrnete více úrovní relací:
var order = await context.Orders
.Include(o => o.OptionalInfo!)
.ThenInclude(op => op.ExtraAdditionalInfo)
.SingleAsync();
Pokud zjistíte, že se to dělá hodně a typy entit, které se v EF Core dotazech používají převážně (nebo výhradně), zvažte konfiguraci navigačních vlastností tak, aby nebylo možné je nastavit na null, a nakonfigurujte je jako volitelné prostřednictvím rozhraní Fluent API a datových anotací. Tím se odstraní všechna upozornění kompilátoru při zachování nepovinného vztahu; Pokud se ale vaše entity procházejí mimo EF Core, můžete pozorovat null hodnoty, i když jsou vlastnosti označeny jako nenulové.
Omezení ve starších verzích
Před EF Core 6.0 se použila následující omezení:
- Veřejný povrch API nebyl opatřen poznámkami pro nullabilitu (veřejné rozhraní API bylo "nulově oblivní"), což z něj dělalo někdy nepohodlné používat, když je funkce NRT zapnutá. To zejména zahrnuje asynchronní operátory LINQ poskytované EF Core, jako FirstOrDefaultAsync. Veřejné rozhraní API je od EF Core 6.0 plně opatřeno anotacemi nulovatelnosti.
- Zpětná analýza nepodporovala nulovatelné referenční typy C# 8 (NRT): EF Core vždy generoval kód v C#, který předpokládal, že funkce je vypnutá. Například textové sloupce s možnou hodnotou null byly vygenerovány jako vlastnost typu
string, nikolistring?, přičemž buď Fluent API, nebo datové anotace byly použity ke konfiguraci, zda je vlastnost povinná. Pokud používáte starší verzi EF Core, můžete upravit vygenerovaný kód a nahradit tyto části anotacemi nulovatelnosti v jazyce C#.