Udostępnij za pośrednictwem


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 element Blog ma jeden BlogHeaderelement i BlogHeader należy do pojedynczego Blogelementu .

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 jednego.

Napiwek

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:

Napiwek

Nie zawsze jest oczywiste, która strona relacji jeden do jednego powinna być podmiotem zabezpieczeń, 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 nadrzędna/podrzędna, element podrzędny 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 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ę 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 żadnej jednostki zależnej. Oznacza to, że wymagana relacja nie wskazuje, że zawsze będzie 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 zależnym. 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 — jeden od zależnego 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 BlogHeader jest odnajdywane 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 jest dopuszczana do 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ń dopuszczanych wartości null w języku C# nawigacja od podmiotu zależnego od podmiotu zabezpieczeń musi mieć wartość null, jeśli właściwość klucza obcego ma wartość null. Jeśli właściwość klucza obcego jest niepusta, nawigacja może mieć wartość null lub nie. W takim przypadku BlogHeader.BlogId wartość jest niepusta, a BlogHeader.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ć. Na 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 typ jednostki głównej (Blog). Podobnie jak w przypadku wszystkich relacji, zamiast tego jest to dokładnie równoważne z typem jednostki zależnej (BlogHeader). Na 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ę.

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 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 podmiotu zabezpieczeń są teraz dopuszczane do wartości null. Powoduje to, że relacja jest "opcjonalna", ponieważ zależna (BlogHeader) nie może być powiązana z żadnym podmiotem zabezpieczeń (Blog), ustawiając jej właściwość klucza obcego i nawigację na wartość null.

Ważne

W przypadku używania typów odwołań dopuszczanych wartości null w języku C# właściwość nawigacji zależna od podmiotu zabezpieczeń musi mieć wartość null, jeśli właściwość klucza obcego ma wartość null. W takim przypadku BlogHeader.BlogId wartość jest dopuszczana do wartości null, więc BlogHeader.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ć. Na 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, zależny koniec relacji jeden do jednego może używać jej właściwości klucza podstawowego lub właściwości jako właściwości lub właściwości klucza obcego. Jest to często nazywane relacją PK-to-PK. Jest to możliwe tylko wtedy, gdy typy podmiotów zabezpieczeń i zależnych mają te same typy kluczy podstawowych, a wynikowa relacja jest zawsze wymagana, ponieważ klucz podstawowy zależnego nie może mieć wartości null.

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. Na przykład:

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

Napiwek

HasPrincipalKey można również używać w tym celu, ale robi 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ć. Na 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 kluczem obcym w tle

// 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 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 poprzedniego przykładu ten przykład usuwa właściwość klucza obcego z typu jednostki 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 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 lub wymagana, 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 ponownie wymaga pewnej konfiguracji, aby wskazać końce podmiotu zabezpieczeń i zależności:

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ć. Na przykład:

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

Opcjonalnie jeden do jednego z kluczem obcym w tle

// 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 dopuszczana do wartości null, ponieważ są używane typy odwołań dopuszczane do wartości null w języku C#, a nawigacja na zależnym typie jednostki jest dopuszczana do wartości 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 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 poprzednio, ta relacja wymaga pewnej konfiguracji, aby wskazać końce podmiotu zabezpieczeń i zależności:

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ć. Na 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 podmiotu zabezpieczeń

// 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 na zależnym została usunięta.

Napiwek

Relacja z tylko jedną nawigacją — od zależnego od podmiotu zabezpieczeń lub od podmiotu zabezpieczeń do zależnego, ale nie obu — jest nazywana relacją jednokierunkową.

Ta relacja jest odnajdywane zgodnie z konwencją, ponieważ klucz obcy jest wykrywany, 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ć. Na 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 metody nie WithOne 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<>() . Na 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 podmiotu zabezpieczeń i z kluczem obcym w tle

// 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 nawigację na zależności.

Tak jak poprzednio, ta relacja wymaga pewnej konfiguracji, aby wskazać końce podmiotu zabezpieczeń i zależności:

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. Na 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żności

// 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 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.

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ć. Na 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<>() . Na 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. Na przykład:

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

W przypadku tej konfiguracji właściwość jest nadal wykrywana jako klucz obcy zgodnie z konwencją, a relacja jest "wymagana", BlogHeader.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>()
        .HasOne<BlogHeader>()
        .WithOne()
        .HasForeignKey<BlogHeader>(e => e.BlogId)
        .IsRequired();
}

Jeden do jednego 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. Na 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 OnModelCreating przy użyciu wywołania metody HasPrincipalKey. Na 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 połączyć z innymi wywołaniami w celu jawnego skonfigurowania nawigacji, właściwości klucza obcego i wymaganego/opcjonalnego charakteru. Na 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 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. Na 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 odnajdywane 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. Na 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 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. Na 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();
        });
}

Napiwek

W powyższym kodzie wywołania metody HasKey i HasOne 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.

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ż zależność nie może istnieć w bazie danych po usunięciu podmiotu zabezpieczeń. Bazę danych można skonfigurować tak, aby wygenerować błąd, zazwyczaj ulega awarii aplikacji, zamiast automatycznie usuwać wiersze zależne, które już nie mogą 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żda z nich Person jest opcjonalnie powiązana z innym Personelementem .

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 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ć. Na 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

W przypadku relacji "jeden do jednego" odwołujących się do siebie, ponieważ typy jednostek głównych i zależnych są takie same, określając, który typ zawiera klucz obcy, nie wyjaśnia końca zależnego. W takim przypadku nawigacja określona w HasOne punktach zależnych od podmiotu zabezpieczeń i nawigacja określona w WithOne punktach od podmiotu zabezpieczeń do zależności.