Navigace mezi relacemi

Relace EF Core jsou definovány cizími klíči. Navigace jsou vrstvené nad cizími klíči, aby poskytovaly přirozené, objektově orientované zobrazení pro čtení a manipulaci s relacemi. Pomocí navigace můžou aplikace pracovat s grafy entit, aniž by se museli zabývat tím, co se děje s hodnotami cizího klíče.

Důležité

Navigace nemůže sdílet více relací. Ke každému cizímu klíči je možné přidružit maximálně jednu navigaci z objektu zabezpečení do závislého objektu a maximálně jednu navigaci ze závislého na objektu zabezpečení.

Tip

Není nutné provádět navigace virtuální, pokud nejsou používány opožděným načítáním nebo proxy servery pro sledování změn.

Referenční navigace

Navigace jsou ve dvou formulářích – odkazy a kolekce. Navigace s odkazy jsou jednoduché odkazy na objekty na jinou entitu. Představují strany "1" relací 1:N a 1:1 . Příklad:

public Blog TheBlog { get; set; }

Referenční navigace musí mít setter, i když nemusí být veřejné. Referenční navigace by neměly být automaticky inicializovány na výchozí hodnotu, která nemá hodnotu null; je ekvivalentní k tvrzení, že entita existuje, pokud neexistuje.

Pokud používáte odkazové typy s možnou hodnotou null jazyka C#, musí být navigace odkazů s možnou hodnotou null pro volitelné relace:

public Blog? TheBlog { get; set; }

Referenční navigace pro požadované relace můžou mít hodnotu null nebo nenulovou hodnotu.

Navigace v kolekcích

Navigace kolekcí jsou instancemi typu kolekce .NET; to znamená, že jakýkoli typ implementuje ICollection<T>. Kolekce obsahuje instance souvisejícího typu entity, z nichž může být libovolné číslo. Představují strany N relací 1:N a M:N . Příklad:

public ICollection<Post> ThePosts { get; set; }

Navigace v kolekcích nemusí mít setter. Inicializace kolekce je běžná, takže je nutné někdy zkontrolovat, zda je nullvlastnost . Příklad:

public ICollection<Post> ThePosts { get; } = new List<Post>();

Tip

Nechtěně nevytvořte vlastnost typu bodyied výrazu, například public ICollection<Post> ThePosts => new List<Post>();. Tím se vytvoří nová prázdná instance kolekce pokaždé, když je vlastnost přístupná, a proto bude zbytečná jako navigace.

Typy kolekcí

Základní instance kolekce musí implementovat ICollection<T>a musí mít pracovní Add metodu. Běžně se používá List<T> nebo HashSet<T>. List<T> je efektivní pro malý počet souvisejících entit a udržuje stabilní řazení. HashSet<T> má efektivnější vyhledávání pro velký počet entit, ale nemá stabilní řazení. Můžete také použít vlastní implementaci kolekce.

Důležité

Kolekce musí používat rovnost odkazů. Při vytváření HashSet<T> navigace v kolekci nezapomeňte použít ReferenceEqualityComparer.

Pole nelze použít pro navigaci v kolekci, protože i když implementují ICollection<T>, Add metoda vyvolá výjimku při zavolání.

I když instance kolekce musí být , ICollection<T>kolekce nemusí být vystavena jako taková. Je například běžné zveřejnit navigaci jako zobrazení IEnumerable<T>, které poskytuje zobrazení jen pro čtení, které nelze náhodně upravit kódem aplikace. Příklad:

public class Blog
{
    public int Id { get; set; }
    public IEnumerable<Post> ThePosts { get; } = new List<Post>();
}

Varianta tohoto modelu zahrnuje metody pro manipulaci s kolekcí podle potřeby. Příklad:

public class Blog
{
    private readonly List<Post> _posts = new();

    public int Id { get; set; }

    public IEnumerable<Post> Posts => _posts;

    public void AddPost(Post post) => _posts.Add(post);
}

Kód aplikace může stále přetypovat vystavenou kolekci na ICollection<T> kolekci a pak s ní manipulovat. Pokud se jedná o problém, může entita vrátit obrannou kopii kolekce. Příklad:

public class Blog
{
    private readonly List<Post> _posts = new();

    public int Id { get; set; }

    public IEnumerable<Post> Posts => _posts.ToList();

    public void AddPost(Post post) => _posts.Add(post);
}

Pečlivě zvažte, jestli je hodnota získaná z této hodnoty dostatečně vysoká, že převáží režii při vytváření kopie kolekce při každém přístupu k navigaci.

Tip

Tento konečný vzor funguje, protože ef ve výchozím nastavení přistupuje k kolekci prostřednictvím jejího backingového pole. To znamená, že EF sám přidává a odebírá entity ze skutečné kolekce, zatímco aplikace pracují pouze s obrannou kopií kolekce.

Inicializace navigace v kolekci

Navigace kolekcí je možné inicializovat podle typu entity, a to buď dychtivě:

public class Blog
{
    public ICollection<Post> Posts { get; } = new List<Post>();
}

Nebo lazily:

public class Blog
{
    private ICollection<Post>? _posts;

    public ICollection<Post> Posts => _posts ??= new List<Post>();
}

Pokud EF potřebuje přidat entitu do navigace v kolekci, například při provádění dotazu, inicializuje kolekci, pokud je aktuálně null. Vytvořená instance závisí na vystaveného typu navigace.

  • Pokud je navigace vystavena jako , HashSet<T>vytvoří se instance HashSet<T> použití ReferenceEqualityComparer .
  • V opačném případě, pokud je navigace vystavena jako konkrétní typ s konstruktorem bez parametrů, je vytvořena instance tohoto konkrétního typu. To platí i pro List<T>jiné typy kolekcí, včetně vlastních typů kolekcí.
  • V opačném případě, pokud je navigace vystavena IEnumerable<T>jako , ICollection<T>ISet<T>nebo nebo , pak je vytvořena instance HashSet<T> použitíReferenceEqualityComparer.
  • V opačném případě, pokud je navigace vystavena IList<T>jako , pak je vytvořena instance List<T> .
  • V opačném případě je vyvolána výjimka.

Poznámka:

Pokud se používají entity oznámení, včetně proxy pro sledování změn, pak ObservableCollection<T> se použijí List<T> místo ObservableHashSet<T> a HashSet<T>.

Důležité

Jak je popsáno v dokumentaci ke sledování změn, EF sleduje pouze jednu instanci jakékoli entity s danou hodnotou klíče. To znamená, že kolekce používané jako navigace musí používat sémantiku rovnosti odkazů. Typy entit, které nepřepíší rovnost objektů, se ve výchozím nastavení získají. Nezapomeňte použít ReferenceEqualityComparer při vytváření HashSet<T> pro použití jako navigaci, abyste měli jistotu, že funguje pro všechny typy entit.

Konfigurace navigace

Navigace jsou součástí modelu jako součást konfigurace relace. To znamená, že konvencí nebo pomocí HasOneatdHasMany. v rozhraní API pro vytváření modelů. Většina konfigurací přidružených k navigaci se provádí konfigurací samotné relace.

Existují však některé typy konfigurace, které jsou specifické pro samotné navigační vlastnosti, nikoli jako součást celkové konfigurace relace. Tento typ konfigurace se provádí pomocí Navigation metody. Pokud chcete například vynutit, aby ef přistupoval k navigaci prostřednictvím jeho vlastnosti, a ne k použití backingového pole:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .Navigation(e => e.Posts)
        .UsePropertyAccessMode(PropertyAccessMode.Property);

    modelBuilder.Entity<Post>()
        .Navigation(e => e.Blog)
        .UsePropertyAccessMode(PropertyAccessMode.Property);
}

Poznámka:

Volání Navigation nelze použít k vytvoření navigační vlastnosti. Slouží pouze ke konfiguraci navigační vlastnosti, která byla dříve vytvořena definováním relace nebo z konvence.

Požadované navigace

Navigace ze závislého objektu na objekt zabezpečení je vyžadována, pokud je relace požadovaná, což znamená, že vlastnost cizího klíče není nullable. Naopak navigace je volitelná, pokud je cizí klíč nullable a relace je proto volitelná.

Referenční navigace z objektu zabezpečení na závislé se liší. Ve většině případů může hlavní entita existovat vždy bez závislých entit. To znamená, že povinný vztah neznamená, že vždy bude alespoň jedna závislá entita. V modelu EF neexistuje žádný způsob a také standardní způsob v relační databázi, aby se zajistilo, že je objekt zabezpečení přidružený k určitému počtu závislých položek. Pokud je to potřeba, musí se implementovat v aplikační (obchodní) logice.

Toto pravidlo má jednu výjimku – pokud objekt zabezpečení a závislé typy sdílejí stejnou tabulku v relační databázi nebo obsažené v dokumentu. K tomu může dojít u vlastněných typů nebo u nevlastních typů , které sdílejí stejnou tabulku. V tomto případě lze navigační vlastnost z objektu zabezpečení do závislého objektu označit jako povinnou, což znamená, že závislý musí existovat.

Konfigurace navigační vlastnosti podle potřeby se provádí pomocí Navigation metody. Příklad:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .Navigation(e => e.BlogHeader)
        .IsRequired();
}