Udostępnij przez


Relacje jeden do jednego

Relacje jeden do jednego są używane, gdy jedna jednostka jest skojarzona z co najwyżej jedną inną jednostką. Na przykład, Blog ma jeden BlogHeader, a BlogHeader należy do pojedynczego Blog.

Ten dokument jest skonstruowany wokół wielu przykładów. 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 jednego.

Wskazówka

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

Wymagane jeden do jednego

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
    public BlogHeader? Header { get; set; } // Reference navigation to dependent
}

// Dependent (child)
public class BlogHeader
{
    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 jednego składa się z:

  • Co najmniej jedna właściwości klucza podstawowego lub alternatywnego w jednostce głównej. Na przykład Blog.Id.
  • Co najmniej jedna właściwość klucza obcego w jednostce zależnej. Na przykład BlogHeader.BlogId.
  • Opcjonalnie nawigacja referencyjna w jednostce głównej odwołującej się do jednostki zależnej. Na przykład Blog.Header.
  • Opcjonalnie nawigacja referencyjna w jednostce zależnej odwołującej się do jednostki głównej. Na przykład BlogHeader.Blog.

Wskazówka

Nie zawsze jest oczywiste, która strona relacji jeden do jednego powinna być stroną główną, a która strona powinna być zależna. Oto kilka zagadnień:

  • Jeśli tabele bazy danych dla dwóch typów już istnieją, tabela z kolumnami kluczy obcych musi być mapowana na typ zależny.
  • Typ jest zwykle typem zależnym, jeśli nie może istnieć logicznie bez innego typu. Na przykład nie ma sensu mieć nagłówka dla bloga, który nie istnieje, więc BlogHeader jest naturalnie typem zależnym.
  • Jeśli istnieje naturalna relacja rodzic/dziecko, to dziecko jest zwykle typem zależnym.

Dlatego dla relacji w tym przykładzie:

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

Uwaga / Notatka

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 żadnej jednostki zależnej. Oznacza to, że wymagana relacja nie wskazuje, że zawsze będzie jednostka zależna. W modelu EF nie ma sposobu, a także brak standardowego sposobu w relacyjnej bazie danych, aby zapewnić, że podmiot główny jest powiązany z obiektem zależnym. Jeśli jest to konieczne, należy zaimplementować ją w logice aplikacji (biznesowej). Aby uzyskać więcej informacji, zobacz Wymagane nawigacje .

Wskazówka

Relacja z dwoma kierunkami — jeden od zależnego do głównego i odwrotny od głównego do zależnego — jest nazywana relacją dwukierunkową.

Ta relacja została odkryta poprzez konwencję. Czyli:

  • Blog jest odkrywane jako główny podmiot w relacji, a BlogHeader jest odkrywane jako zależne.
  • BlogHeader.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ż BlogHeader.BlogId nie może mieć wartości null.
  • Blog.BlogHeader jest odnajdywane jako nawigacja referencyjna.
  • BlogHeader.Blog jest odnajdywane jako nawigacja referencyjna.

Ważne

W przypadku używania typów odwołań dopuszczających wartości null w języku C#, nawigacja od zależnego do głównego musi być dopuszczająca wartość null, jeśli właściwość klucza obcego jest dopuszczająca wartość null. Jeśli właściwość klucza obcego jest nienullowalna, nawigacja może być nullowalna lub nie. W tym przypadku BlogHeader.BlogId jest niemogąca przyjąć wartości null, a BlogHeader.Blog także jest niemogąca przyjąć wartości null. Konstrukcja = null!; jest używana, aby oznaczyć to jako intencjonalne dla kompilatora języka C#, ponieważ program EF zazwyczaj ustawia instancję Blog i nie może być null dla w pełni załadowanej relacji. Aby uzyskać więcej informacji, zobacz Praca z typami referencyjnymi, które mogą przyjmować wartość 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>()
        .HasOne(e => e.Header)
        .WithOne(e => e.Blog)
        .HasForeignKey<BlogHeader>(e => e.BlogId)
        .IsRequired();
}

W powyższym przykładzie konfiguracja relacji rozpoczyna się od typu jednostki głównej (Blog). Podobnie jak w przypadku wszystkich relacji, rozpoczęcie od typu jednostki zależnej (BlogHeader) jest dokładnie równoważne. Przykład:

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

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

Wskazówka

Nigdy nie jest konieczne dwukrotne skonfigurowanie relacji, raz zaczynając od głównego, a następnie ponownie rozpoczynając od zależnego. Ponadto próba skonfigurowania głównej i zależnej połowy relacji oddzielnie nie działa. Wybierz konfigurację każdej relacji z jednego lub drugiego końca i następnie napisz kod konfiguracji tylko raz.

Opcjonalnie jeden do jednego

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
    public BlogHeader? Header { get; set; } // Reference navigation to dependent
}

// Dependent (child)
public class BlogHeader
{
    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 obiektu nadrzędnego mogą teraz przyjmować wartość null. Powoduje to, że relacja jest "opcjonalna", ponieważ zależna (BlogHeader) nie może być powiązana z żadnym głównym podmiotem (Blog), poprzez ustawienie jego właściwości klucza obcego i nawigacji na wartość null.

Ważne

W przypadku używania nienullowalnych typów odwołań w języku C# właściwość nawigacyjna od zależnej do głównej musi być nienullowalna, jeśli właściwość klucza obcego jest nienullowalna. W takim przypadku BlogHeader.BlogId może mieć wartość null, więc BlogHeader.Blog musi również mieć wartość null. Aby uzyskać więcej informacji, zobacz Praca z typami referencyjnymi, które mogą przyjmować wartość 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>()
        .HasOne(e => e.Header)
        .WithOne(e => e.Blog)
        .HasForeignKey<BlogHeader>(e => e.BlogId)
        .IsRequired(false);
}

Wymagana relacja "jeden do jednego" z kluczem podstawowym do relacji klucza podstawowego

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
    public BlogHeader? Header { get; set; } // Reference navigation to dependent
}

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

W przeciwieństwie do relacji jeden do wielu, strona zależna w relacji jeden do jednego może używać swoich właściwości klucza podstawowego jako właściwości klucza obcego. Jest to często nazywane relacją PK-to-PK. Jest to możliwe tylko wtedy, gdy typy główne i zależne mają te same typy kluczy głównych, a wynikająca relacja jest zawsze wymagana, ponieważ klucz główny typu zależnego nie może być pusty.

Każda relacja "jeden do jednego", w której klucz obcy nie został odnaleziony zgodnie z konwencją, musi być skonfigurowana tak, aby wskazywała główne i zależne końce relacji. Zazwyczaj odbywa się to za pomocą wywołania metody HasForeignKey. Przykład:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasOne(e => e.Header)
        .WithOne(e => e.Blog)
        .HasForeignKey<BlogHeader>();
}

Wskazówka

HasPrincipalKey można również używać w tym celu, ale jest to mniej powszechne.

Jeśli żadna właściwość nie jest określona w wywołaniu metody HasForeignKey, a klucz podstawowy jest odpowiedni, jest używany jako klucz obcy. 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>()
        .HasOne(e => e.Header)
        .WithOne(e => e.Blog)
        .HasForeignKey<BlogHeader>(e => e.Id)
        .IsRequired();
}

Wymagany jeden do jednego z ukrytym kluczem obcym

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
    public BlogHeader? Header { get; set; } // Reference navigation to dependent
}

// Dependent (child)
public class BlogHeader
{
    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 drogą elektroniczną, wartości klucza obcego mogą być przydatnym sposobem zachowywania informacji o relacjach, gdy jednostki nie znajdują się w formie obiektu. Dlatego często jest pragmatyczne zachowanie właściwości klucza obcego w .NET typie dla tego 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, aby jego wartość mogła być związana z jednostką.

Na podstawie poprzedniego przykładu, ten przykład usuwa właściwość klucza obcego z typu encji zależnej. Jednak zamiast używać klucza podstawowego, zamiast tego program EF jest instruowany, aby utworzyć właściwość klucza obcego w tle o nazwie BlogId typu int.

Należy zwrócić uwagę, że używane są typy odwołań dopuszczające wartości null w języku C#, dzięki czemu nullowalność nawigacji od zależnego do głównego jest wykorzystywana do określenia, czy właściwość klucza obcego jest nullowalna, a tym samym, czy relacja jest opcjonalna, czy wymagana. 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ę.

Ta relacja znów wymaga pewnej konfiguracji, aby wskazać główne i zależne końce.

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasOne(e => e.Header)
        .WithOne(e => e.Blog)
        .HasForeignKey<BlogHeader>("BlogId");
}

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>()
        .HasOne(e => e.Header)
        .WithOne(e => e.Blog)
        .HasForeignKey<BlogHeader>("BlogId")
        .IsRequired();
}

Opcjonalna relacja jeden do jednego z ukrytym kluczem obcym

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
    public BlogHeader? Header { get; set; } // Reference navigation to dependent
}

// Dependent (child)
public class BlogHeader
{
    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. Jednak w przeciwieństwie do poprzedniego przykładu, tym razem właściwość klucza obcego jest tworzona jako nullable, ponieważ używane są typy odwołań nullable w języku C#, a nawigacja w zależnym typie jednostki jest nullable. Powoduje to, że relacja jest opcjonalna.

Jeśli w C# nie są używane typy odwołań nullable, właściwość klucza obcego zostanie domyślnie utworzona jako nullable. Oznacza to, że relacje z automatycznie utworzonymi właściwościami cieniowymi są domyślnie opcjonalne.

Tak jak poprzednio, ta relacja wymaga pewnej konfiguracji, aby wskazać główne i zależne końce.

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasOne(e => e.Header)
        .WithOne(e => e.Blog)
        .HasForeignKey<BlogHeader>("BlogId");
}

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>()
        .HasOne(e => e.Header)
        .WithOne(e => e.Blog)
        .HasForeignKey<BlogHeader>("BlogId")
        .IsRequired(false);
}

Jeden do jednego bez nawigacji do głównego elementu

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
    public BlogHeader? Header { get; set; } // Reference navigation to dependent
}

// Dependent (child)
public class BlogHeader
{
    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 zależnego obiektu została usunięta.

Wskazówka

Relacja z wyłącznie jedną nawigacją — od zależnego do głównego lub od głównego do zależnego, ale nie obu — jest nazywana relacją jednokierunkową.

Ta relacja jest odkryta zgodnie z konwencją, ponieważ wykryty jest klucz obcy, wskazując tym samym stronę zależną. 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>()
        .HasOne(e => e.Header)
        .WithOne()
        .HasForeignKey<BlogHeader>(e => e.BlogId)
        .IsRequired();
}

Zwróć uwagę, że wywołanie do WithOne nie ma argumentów. Jest to sposób, aby poinformować EF, że nie ma nawigacji z BlogHeader 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<BlogHeader>()
        .HasOne<Blog>()
        .WithOne(e => e.Header)
        .HasForeignKey<BlogHeader>(e => e.BlogId)
        .IsRequired();
}

Jeden do jednego bez nawigacji do głównego i z ukrytym kluczem obcym

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
    public BlogHeader? Header { get; set; } // Reference navigation to dependent
}

// Dependent (child)
public class BlogHeader
{
    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 właściwość nawigacji na stronie zależnej.

Tak jak poprzednio, ta relacja wymaga pewnej konfiguracji, aby wskazać główne i zależne końce.

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasOne(e => e.Header)
        .WithOne()
        .HasForeignKey<BlogHeader>("BlogId")
        .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>()
        .HasOne(e => e.Header)
        .WithOne()
        .HasForeignKey<BlogHeader>("BlogId")
        .IsRequired();
}

Jeden do jednego bez nawigacji do zależnych

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

// Dependent (child)
public class BlogHeader
{
    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 nadrzędnego do podmiotów zależnych, ale nie było nawigacji od podmiotu zależnego do podmiotu nadrzędnego. W przypadku następnych kilku przykładów nawigacja dla zależnych zostanie ponownie wprowadzona, a nawigacja dla głównego zostanie usunięta.

Zgodnie z konwencją EF będzie traktować to jako relację jeden do wielu. Wymagana jest minimalna konfiguracja, aby uczynić ją "jeden do jednego":

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

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

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<BlogHeader>()
        .HasOne(e => e.Blog)
        .WithOne()
        .HasForeignKey<BlogHeader>(e => e.BlogId)
        .IsRequired();
}

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<Blog>()
        .HasOne<BlogHeader>()
        .WithOne(e => e.Blog)
        .HasForeignKey<BlogHeader>(e => e.BlogId)
        .IsRequired();
}

Jeden do jednego 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 BlogHeader
{
    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>()
        .HasOne<BlogHeader>()
        .WithOne();
}

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

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

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

Jeden do jednego z kluczem alternatywnym

We wszystkich dotychczasowych przykładach, właściwość klucza obcego dla elementu zależnego jest ograniczona do właściwości klucza głównego dla elementu głównego. 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 BlogHeader.BlogId foreign key
    public BlogHeader? Header { get; set; } // Reference navigation to dependent
}

// Dependent (child)
public class BlogHeader
{
    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 w OnModelCreating przy użyciu wywołania HasPrincipalKey. Przykład:

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

HasPrincipalKey można łączyć z innymi wywołaniami, aby jawnie skonfigurować nawigacje, właściwości kluczy obcych oraz wybrać, czy mają być wymagane czy opcjonalne. Przykład:

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

Jeden do jednego z złożonym kluczem obcym

We wszystkich przykładach do tej pory właściwość klucza podstawowego lub alternatywnego głównego elementu 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 relacji ma klucz złożony, klucz obcy podmiotu zależnego musi również być 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 BlogHeader? Header { get; set; } // Reference navigation to dependent
}

// Dependent (child)
public class BlogHeader
{
    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 odkrywana zgodnie z konwencją. Zostanie on jednak odnaleziony tylko wtedy, gdy klucz złożony został jawnie skonfigurowany, ponieważ klucze złożone nie są odnajdywane automatycznie. Przykład:

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 uznawana za null, jeśli którakolwiek z wartości jego właściwości jest 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.HasOne(e => e.Header)
                .WithOne(e => e.Blog)
                .HasPrincipalKey<Blog>(e => new { e.Id1, e.Id2 })
                .HasForeignKey<BlogHeader>(e => new { e.BlogId1, e.BlogId2 })
                .IsRequired();
        });
}

Wskazówka

W powyższym kodzie wywołania metody HasKey i HasOne zostały zgrupowane razem w zagnieżdżonym konstruktorze. Konstruktory zagnieżdżone eliminują potrzebę wielokrotnego wywoływania Entity<>() dla tego samego typu encji, ale są funkcjonalnie równoważne wielokrotnemu wywołaniu Entity<>().

Wymagane jeden do jednego bez kaskadowego usuwania

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
    public BlogHeader? Header { get; set; } // Reference navigation to dependent
}

// Dependent (child)
public class BlogHeader
{
    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. Dzieje się tak, ponieważ osoba zależna nie może istnieć w bazie danych po usunięciu elementu nadrzędnego. Bazę danych można skonfigurować tak, aby wygenerować błąd, co zwykle powoduje awarię aplikacji, zamiast automatycznie usuwać wiersze zależne, które nie mogą już istnieć. Wymaga to pewnej konfiguracji:

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

Samodzielne odwoływanie się do jednego

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żdy Person jest opcjonalnie powiązany z innym Person.

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

    public int? HusbandId { get; set; } // Optional foreign key property
    public Person? Husband { get; set; } // Optional reference navigation to principal
    public Person? Wife { get; set; } // Reference navigation to dependent
}

Ta relacja została odkryta poprzez 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<Person>()
        .HasOne(e => e.Husband)
        .WithOne(e => e.Wife)
        .HasForeignKey<Person>(e => e.HusbandId)
        .IsRequired(false);
}

Uwaga / Notatka

W przypadku relacji "jeden do jednego" odwołujących się do siebie, ponieważ typy jednostek głównych i zależnych są takie same, określenie, który typ zawiera klucz obcy, nie precyzuje części zależnej. W takim przypadku nawigacja określona w HasOne wskazuje od zależnych do głównego, a nawigacja określona w WithOne wskazuje od głównego do zależnych.