Relacje jeden-do-wielu

Relacje jeden do wielu są używane, gdy jedna jednostka jest skojarzona z dowolną liczbą innych jednostek. Na przykład może mieć wiele skojarzonych elementów Posts, Blog ale każda z nich Post jest skojarzona tylko z jednym Blogelementem .

Ten dokument jest ustrukturyzowany w wielu przykładach. Przykłady zaczynają się od typowych przypadków, które również wprowadzają koncepcje. W kolejnych przykładach opisano mniej typowe rodzaje konfiguracji. Dobrym podejściem jest zrozumienie kilku pierwszych przykładów i pojęć, a następnie przejście do późniejszych przykładów na podstawie konkretnych potrzeb. W oparciu o to podejście zaczniemy od prostych "wymaganych" i "opcjonalnych" relacji jeden do wielu.

Napiwek

Kod dla wszystkich poniższych przykładów można znaleźć w pliku OneToMany.cs.

Wymagany jeden do wielu

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
    public ICollection<Post> Posts { get; } = new List<Post>(); // Collection navigation containing dependents
}

// Dependent (child)
public class Post
{
    public int Id { get; set; }
    public int BlogId { get; set; } // Required foreign key property
    public Blog Blog { get; set; } = null!; // Required reference navigation to principal
}

Relacja jeden do wielu składa się z:

  • Co najmniej jedna właściwości klucza podstawowego lub alternatywnego w jednostce głównej, czyli "jeden" koniec relacji. Na przykład Blog.Id.
  • Co najmniej jedna właściwości klucza obcego dla jednostki zależnej, czyli "wiele" końca relacji. Na przykład Post.BlogId.
  • Opcjonalnie nawigacja kolekcji w jednostce głównej odwołującej się do jednostek zależnych. Na przykład Blog.Posts.
  • Opcjonalnie nawigacja referencyjna w jednostce zależnej odwołującej się do jednostki głównej. Na przykład Post.Blog.

Dlatego dla relacji w tym przykładzie:

  • Właściwość Post.BlogId klucza obcego nie może zawierać wartości null. Powoduje to, że relacja jest "wymagana", ponieważ każda zależna (Post) musi być powiązana z pewnym podmiotem zabezpieczeń (Blog), ponieważ jej właściwość klucza obcego musi być ustawiona na pewną wartość.
  • Obie jednostki mają nawigacje wskazujące powiązaną jednostkę lub jednostki po drugiej stronie relacji.

Uwaga

Wymagana relacja gwarantuje, że każda jednostka zależna musi być skojarzona z pewną jednostką główną. Jednak jednostka główna może zawsze istnieć bez żadnych jednostek zależnych. Oznacza to, że wymagana relacja nie wskazuje, że zawsze będzie istnieć co najmniej jedna jednostka zależna. W modelu EF nie ma możliwości, a także nie ma standardowego sposobu w relacyjnej bazie danych, aby upewnić się, że podmiot zabezpieczeń jest skojarzony z określoną liczbą zależności. Jeśli jest to konieczne, należy zaimplementować ją w logice aplikacji (biznesowej). Aby uzyskać więcej informacji, zobacz Wymagane nawigacje .

Napiwek

Relacja z dwoma nawigacjami, jedna z zależności od podmiotu zabezpieczeń i odwrotność od podmiotu zabezpieczeń do zależności, jest nazywana relacją dwukierunkową.

Ta relacja jest odnajdywane zgodnie z konwencją. To znaczy:

  • Blog jest odnajdywane jako podmiot zabezpieczeń w relacji i Post jest odnajdywane jako zależne.
  • Post.BlogId jest odnajdywane jako klucz obcy zależnego odwołującego Blog.Id się do klucza podstawowego podmiotu zabezpieczeń. Relacja jest wykrywana zgodnie z wymaganiami, ponieważ Post.BlogId nie jest dopuszczana do wartości null.
  • Blog.Posts jest odnajdywane jako nawigacja po kolekcji.
  • Post.Blog jest odnajdywane jako nawigacja referencyjna.

Ważne

W przypadku używania typów odwołań dopuszczanych wartości null w języku C# nawigacja referencyjna musi mieć wartość null, jeśli właściwość klucza obcego ma wartość null. Jeśli właściwość klucza obcego jest niepusta, nawigacja referencyjna może mieć wartość null lub nie. W takim przypadku Post.BlogId wartość jest niepusta, a Post.Blog także nie może zawierać wartości null. Konstrukcja = null!; jest używana do oznaczania tego jako zamierzonego dla kompilatora języka C#, ponieważ program EF zazwyczaj ustawia Blog wystąpienie i nie może mieć wartości null dla w pełni załadowanej relacji. Aby uzyskać więcej informacji, zobacz Praca z typami referencyjnymi dopuszczanymi wartościami null.

W przypadkach, gdy nawigacja, klucz obcy lub wymagany/opcjonalny charakter relacji nie są odnajdywane zgodnie z konwencją, te elementy można jawnie skonfigurować. Przykład:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasMany(e => e.Posts)
        .WithOne(e => e.Blog)
        .HasForeignKey(e => e.BlogId)
        .IsRequired();
}

W powyższym przykładzie konfiguracja relacji zaczyna się od HasMany w typie jednostki głównej (Blog), a następnie następuje zgodnie z instrukcjami WithOne. Podobnie jak w przypadku wszystkich relacji, jest to dokładnie równoważne, aby rozpocząć od typu jednostki zależnej (Post) i używać HasOne po nim .WithMany Przykład:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Post>()
        .HasOne(e => e.Blog)
        .WithMany(e => e.Posts)
        .HasForeignKey(e => e.BlogId)
        .IsRequired();
}

Żadna z tych opcji nie jest lepsza niż druga; obie te elementy powodują dokładnie taką samą konfigurację.

Napiwek

Nigdy nie jest konieczne dwukrotne skonfigurowanie relacji, raz zaczynając od podmiotu zabezpieczeń, a następnie ponownie rozpoczynając od zależności. Ponadto próba skonfigurowania głównej i zależnej połowy relacji oddzielnie nie działa. Wybierz, aby skonfigurować każdą relację z jednego końca lub drugiego, a następnie napisać kod konfiguracji tylko raz.

Opcjonalnie jeden do wielu

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
    public ICollection<Post> Posts { get; } = new List<Post>(); // Collection navigation containing dependents
}

// Dependent (child)
public class Post
{
    public int Id { get; set; }
    public int? BlogId { get; set; } // Optional foreign key property
    public Blog? Blog { get; set; } // Optional reference navigation to principal
}

Jest to takie samo jak w poprzednim przykładzie, z tą różnicą, że właściwość klucza obcego i nawigacja do podmiotu zabezpieczeń są teraz dopuszczane do wartości null. Powoduje to, że relacja jest "opcjonalna", ponieważ zależność (Post) może istnieć bez powiązania z żadnym podmiotem zabezpieczeń (Blog).

Ważne

W przypadku używania typów odwołań dopuszczanych wartości null w języku C# nawigacja referencyjna musi mieć wartość null, jeśli właściwość klucza obcego ma wartość null. W takim przypadku Post.BlogId wartość jest dopuszczana do wartości null, więc Post.Blog musi być również dopuszczana wartość null. Aby uzyskać więcej informacji, zobacz Praca z typami referencyjnymi dopuszczanymi wartościami null.

Tak jak wcześniej ta relacja została odkryta zgodnie z konwencją. W przypadkach, gdy nawigacja, klucz obcy lub wymagany/opcjonalny charakter relacji nie są odnajdywane zgodnie z konwencją, te elementy można jawnie skonfigurować. Przykład:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasMany(e => e.Posts)
        .WithOne(e => e.Blog)
        .HasForeignKey(e => e.BlogId)
        .IsRequired(false);
}

Wymagany jeden do wielu z kluczem obcym w tle

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
    public ICollection<Post> Posts { get; } = new List<Post>(); // Collection navigation containing dependents
}

// Dependent (child)
public class Post
{
    public int Id { get; set; }
    public Blog Blog { get; set; } = null!; // Required reference navigation to principal
}

W niektórych przypadkach możesz nie chcieć właściwości klucza obcego w modelu, ponieważ klucze obce są szczegółowymi informacjami o tym, jak relacja jest reprezentowana w bazie danych, co nie jest potrzebne w przypadku używania relacji w sposób czysto obiektowy. Jeśli jednak jednostki będą serializowane, na przykład w celu wysłania za pośrednictwem przewodu, wartości klucza obcego mogą być przydatnym sposobem przechowywania informacji o relacji bez zmian, gdy jednostki nie znajdują się w formularzu obiektu. Dlatego często pragmatyczne zachowanie właściwości klucza obcego w typie platformy .NET w tym celu. Właściwości klucza obcego mogą być prywatne, co jest często dobrym kompromisem, aby uniknąć ujawnienia klucza obcego, pozwalając jednocześnie na podróż z jednostką.

Po wykonaniu dwóch poprzednich przykładów ten przykład usuwa właściwość klucza obcego z typu jednostki zależnej. W związku z tym ef tworzy właściwość klucza obcego w tle o nazwie BlogId typu int.

Należy pamiętać, że używane są typy odwołań dopuszczające wartość null w języku C#, dlatego do określenia, czy właściwość klucza obcego jest opcjonalna, czy relacja jest opcjonalna, czy wymagana jest wartość null. Jeśli typy odwołań dopuszczane do wartości null nie są używane, właściwość klucza obcego w tle będzie domyślnie dopuszczana do wartości null, co spowoduje, że relacja będzie domyślnie opcjonalna. W tym przypadku użyj polecenia IsRequired , aby wymusić, aby właściwość klucza obcego w tle nie może być dopuszczana do wartości null i wprowadzić wymaganą relację.

Tak jak wcześniej ta relacja została odkryta zgodnie z konwencją. W przypadkach, gdy nawigacja, klucz obcy lub wymagany/opcjonalny charakter relacji nie są odnajdywane zgodnie z konwencją, te elementy można jawnie skonfigurować. Przykład:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasMany(e => e.Posts)
        .WithOne(e => e.Blog)
        .HasForeignKey("BlogId")
        .IsRequired();
}

Opcjonalnie jeden do wielu z kluczem obcym w tle

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
    public ICollection<Post> Posts { get; } = new List<Post>(); // Collection navigation containing dependents
}

// Dependent (child)
public class Post
{
    public int Id { get; set; }
    public Blog? Blog { get; set; } // Optional reference navigation to principal
}

Podobnie jak w poprzednim przykładzie właściwość klucza obcego została usunięta z typu jednostki zależnej. W związku z tym ef tworzy właściwość klucza obcego w tle o nazwie BlogId typu int?. W przeciwieństwie do poprzedniego przykładu tym razem właściwość klucza obcego jest tworzona jako dopuszczana do wartości null, ponieważ są używane typy odwołań dopuszczane do wartości null w języku C#, a nawigacja na typie jednostki zależnej ma wartość null. Powoduje to, że relacja jest opcjonalna.

Jeśli typy odwołań dopuszczane do wartości null w języku C# nie są używane, właściwość klucza obcego również zostanie domyślnie utworzona jako dopuszczana do wartości null. Oznacza to, że relacje z automatycznie utworzonymi właściwościami w tle są opcjonalne domyślnie.

Tak jak wcześniej ta relacja została odkryta zgodnie z konwencją. W przypadkach, gdy nawigacja, klucz obcy lub wymagany/opcjonalny charakter relacji nie są odnajdywane zgodnie z konwencją, te elementy można jawnie skonfigurować. Przykład:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasMany(e => e.Posts)
        .WithOne(e => e.Blog)
        .HasForeignKey("BlogId")
        .IsRequired(false);
}

Jeden do wielu bez nawigacji do podmiotu zabezpieczeń

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
    public ICollection<Post> Posts { get; } = new List<Post>(); // Collection navigation containing dependents
}

// Dependent (child)
public class Post
{
    public int Id { get; set; }
    public int BlogId { get; set; } // Required foreign key property
}

W tym przykładzie właściwość klucza obcego została ponownie wprowadzona, ale nawigacja na zależnym została usunięta.

Napiwek

Relacja z tylko jedną nawigacją, od zależności do podmiotu zabezpieczeń lub od podmiotu zabezpieczeń do zależności, ale nie obie, jest nazywana relacją jednokierunkową.

Tak jak wcześniej ta relacja została odkryta zgodnie z konwencją. W przypadkach, gdy nawigacja, klucz obcy lub wymagany/opcjonalny charakter relacji nie są odnajdywane zgodnie z konwencją, te elementy można jawnie skonfigurować. Przykład:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasMany(e => e.Posts)
        .WithOne()
        .HasForeignKey(e => e.BlogId)
        .IsRequired();
}

Zwróć uwagę, że wywołanie metody nie WithOne ma argumentów. Jest to sposób, aby poinformować EF, że nie ma nawigacji z Post do Blog.

Jeśli konfiguracja rozpoczyna się od jednostki bez nawigacji, typ jednostki na drugim końcu relacji musi być jawnie określony przy użyciu wywołania ogólnego HasOne<>() . Przykład:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Post>()
        .HasOne<Blog>()
        .WithMany(e => e.Posts)
        .HasForeignKey(e => e.BlogId)
        .IsRequired();
}

Jeden do wielu bez nawigacji do podmiotu zabezpieczeń i z kluczem obcym w tle

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
    public ICollection<Post> Posts { get; } = new List<Post>(); // Collection navigation containing dependents
}

// Dependent (child)
public class Post
{
    public int Id { get; set; }
}

Ten przykład łączy dwa z poprzednich przykładów, usuwając zarówno właściwość klucza obcego, jak i nawigację na zależności.

Ta relacja jest wykrywana zgodnie z konwencją jako opcjonalna relacja. Ponieważ w kodzie nie ma nic, czego można użyć, aby wskazać, że powinno być wymagane, wymagana jest minimalna konfiguracja przy użyciu IsRequired polecenia w celu utworzenia wymaganej relacji. Przykład:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasMany(e => e.Posts)
        .WithOne()
        .IsRequired();
}

Bardziej pełną konfigurację można użyć do jawnego skonfigurowania nawigacji i nazwy klucza obcego z odpowiednim wywołaniem IsRequired() lub IsRequired(false) zgodnie z potrzebami. Przykład:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasMany(e => e.Posts)
        .WithOne()
        .HasForeignKey("BlogId")
        .IsRequired();
}

Jeden do wielu bez nawigacji do zależności

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
}

// Dependent (child)
public class Post
{
    public int Id { get; set; }
    public int BlogId { get; set; } // Required foreign key property
    public Blog Blog { get; set; } = null!; // Required reference navigation to principal
}

Poprzednie dwa przykłady miały nawigacje od podmiotu zabezpieczeń do zależności, ale nie ma nawigacji od podmiotu zależnego od podmiotu zabezpieczeń. W przypadku następnych kilku przykładów nawigacja na zależności zostanie ponownie wprowadzona, a nawigacja na podmiotu zabezpieczeń zostanie usunięta.

Tak jak wcześniej ta relacja została odkryta zgodnie z konwencją. W przypadkach, gdy nawigacja, klucz obcy lub wymagany/opcjonalny charakter relacji nie są odnajdywane zgodnie z konwencją, te elementy można jawnie skonfigurować. Przykład:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Post>()
        .HasOne(e => e.Blog)
        .WithMany()
        .HasForeignKey(e => e.BlogId)
        .IsRequired();
}

Zwróć ponownie uwagę, że WithMany() jest wywoływana bez argumentów, aby wskazać, że w tym kierunku nie ma nawigacji.

Jeśli konfiguracja rozpoczyna się od jednostki bez nawigacji, typ jednostki na drugim końcu relacji musi być jawnie określony przy użyciu wywołania ogólnego HasMany<>() . Przykład:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasMany<Post>()
        .WithOne(e => e.Blog)
        .HasForeignKey(e => e.BlogId)
        .IsRequired();
}

Jeden do wielu bez nawigacji

Czasami może być przydatne skonfigurowanie relacji bez nawigacji. Taką relację można manipulować tylko przez bezpośrednie zmienianie wartości klucza obcego.

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
}

// Dependent (child)
public class Post
{
    public int Id { get; set; }
    public int BlogId { get; set; } // Required foreign key property
}

Ta relacja nie jest wykrywana zgodnie z konwencją, ponieważ nie ma żadnych nawigacji wskazujących, że te dwa typy są powiązane. Można ją jawnie skonfigurować w pliku OnModelCreating. Przykład:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasMany<Post>()
        .WithOne();
}

W przypadku tej konfiguracji właściwość jest nadal wykrywana jako klucz obcy zgodnie z konwencją, a relacja jest wymagana, Post.BlogId ponieważ właściwość klucza obcego nie może mieć wartości null. Relację można wprowadzić jako "opcjonalną", tworząc właściwość klucza obcego z możliwością wartości null.

Bardziej pełną jawną konfiguracją tej relacji jest::

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasMany<Post>()
        .WithOne()
        .HasForeignKey(e => e.BlogId)
        .IsRequired();
}

Jeden do wielu z kluczem alternatywnym

We wszystkich przykładach do tej pory właściwość klucza obcego zależna jest ograniczona do właściwości klucza podstawowego dla podmiotu zabezpieczeń. Klucz obcy może być ograniczony do innej właściwości, która następnie staje się kluczem alternatywnym dla typu jednostki głównej. Przykład:

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
    public int AlternateId { get; set; } // Alternate key as target of the Post.BlogId foreign key
    public ICollection<Post> Posts { get; } = new List<Post>(); // Collection navigation containing dependents
}

// Dependent (child)
public class Post
{
    public int Id { get; set; }
    public int BlogId { get; set; } // Required foreign key property
    public Blog Blog { get; set; } = null!; // Required reference navigation to principal
}

Ta relacja nie jest wykrywana zgodnie z konwencją, ponieważ program EF zawsze, zgodnie z konwencją, tworzy relację z kluczem podstawowym. Można ją skonfigurować jawnie OnModelCreating przy użyciu wywołania metody HasPrincipalKey. Przykład:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasMany(e => e.Posts)
        .WithOne(e => e.Blog)
        .HasPrincipalKey(e => e.AlternateId);
}

HasPrincipalKey można połączyć z innymi wywołaniami w celu jawnego skonfigurowania nawigacji, właściwości klucza obcego i wymaganego/opcjonalnego charakteru. Przykład:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasMany(e => e.Posts)
        .WithOne(e => e.Blog)
        .HasPrincipalKey(e => e.AlternateId)
        .HasForeignKey(e => e.BlogId)
        .IsRequired();
}

Jeden do wielu z złożonym kluczem obcym

We wszystkich przykładach do tej pory właściwość klucza podstawowego lub alternatywnego podmiotu zabezpieczeń składała się z jednej właściwości. Klucze podstawowe lub alternatywne można również utworzyć w postaci więcej niż jednej właściwości — są one nazywane "kluczami złożonymi". Gdy podmiot zabezpieczeń relacji ma klucz złożony, klucz obcy zależnego musi być również kluczem złożonym o tej samej liczbie właściwości. Przykład:

// Principal (parent)
public class Blog
{
    public int Id1 { get; set; } // Composite key part 1
    public int Id2 { get; set; } // Composite key part 2
    public ICollection<Post> Posts { get; } = new List<Post>(); // Collection navigation containing dependents
}

// Dependent (child)
public class Post
{
    public int Id { get; set; }
    public int BlogId1 { get; set; } // Required foreign key property part 1
    public int BlogId2 { get; set; } // Required foreign key property part 2
    public Blog Blog { get; set; } = null!; // Required reference navigation to principal
}

Ta relacja jest odnajdywane zgodnie z konwencją. Jednak sam klucz złożony musi być skonfigurowany jawnie::

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasKey(e => new { e.Id1, e.Id2 });
}

Ważne

Wartość klucza obcego złożonego jest uważana za null null, jeśli którakolwiek z jej wartości właściwości ma wartość null. Złożony klucz obcy z jedną właściwością o wartości null, a inny inny niż null nie będzie traktowany jako dopasowanie klucza podstawowego lub alternatywnego z tymi samymi wartościami. Oba zostaną uznane za null.

Zarówno HasForeignKey , jak i HasPrincipalKey może służyć do jawnego określania kluczy z wieloma właściwościami. Przykład:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>(
        nestedBuilder =>
        {
            nestedBuilder.HasKey(e => new { e.Id1, e.Id2 });

            nestedBuilder.HasMany(e => e.Posts)
                .WithOne(e => e.Blog)
                .HasPrincipalKey(e => new { e.Id1, e.Id2 })
                .HasForeignKey(e => new { e.BlogId1, e.BlogId2 })
                .IsRequired();
        });
}

Napiwek

W powyższym kodzie wywołania metody HasKey i HasMany zostały zgrupowane razem w zagnieżdżonym konstruktorze. Konstruktory zagnieżdżone usuwają konieczność wielokrotnego wywoływania Entity<>() dla tego samego typu jednostki, ale są funkcjonalnie równoważne wywołaniu Entity<>() wiele razy.

Wymagany jeden do wielu bez usuwania kaskadowego

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
    public ICollection<Post> Posts { get; } = new List<Post>(); // Collection navigation containing dependents
}

// Dependent (child)
public class Post
{
    public int Id { get; set; }
    public int BlogId { get; set; } // Required foreign key property
    public Blog Blog { get; set; } = null!; // Required reference navigation to principal
}

Zgodnie z konwencją wymagane relacje są skonfigurowane do usuwania kaskadowego; oznacza to, że po usunięciu podmiotu zabezpieczeń wszystkie jego zależności również są usuwane, ponieważ zależności nie mogą istnieć w bazie danych bez podmiotu zabezpieczeń. Istnieje możliwość skonfigurowania programu EF pod kątem zgłaszania wyjątku zamiast automatycznego usuwania wierszy zależnych, które nie mogą już istnieć:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasMany(e => e.Posts)
        .WithOne(e => e.Blog)
        .OnDelete(DeleteBehavior.Restrict);
}

Samonależące do jednego do wielu

We wszystkich poprzednich przykładach typ jednostki głównej różnił się od typu jednostki zależnej. To nie musi być tak. Na przykład w poniższych typach każda Employee z nich jest powiązana z innymi Employeeselementami .

public class Employee
{
    public int Id { get; set; }

    public int? ManagerId { get; set; } // Optional foreign key property
    public Employee? Manager { get; set; } // Optional reference navigation to principal
    public ICollection<Employee> Reports { get; } = new List<Employee>(); // Collection navigation containing dependents
}

Ta relacja jest odnajdywane zgodnie z konwencją. W przypadkach, gdy nawigacja, klucz obcy lub wymagany/opcjonalny charakter relacji nie są odnajdywane zgodnie z konwencją, te elementy można jawnie skonfigurować. Przykład:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Employee>()
        .HasOne(e => e.Manager)
        .WithMany(e => e.Reports)
        .HasForeignKey(e => e.ManagerId)
        .IsRequired(false);
}