Freigeben über


1:1-Beziehungen

1:1-Beziehungen werden verwendet, wenn eine Entität höchstens einer anderen Entität zugeordnet ist. Ein Beispiel: Ein Blog hat ein BlogHeader, und das BlogHeader gehört zu einem einzelnen Blog.

Dieses Dokument ist in vielen Beispielen strukturiert. Die Beispiele beginnen mit allgemeinen Fällen, die auch Konzepte einführen. In späteren Beispielen werden weniger häufige Konfigurationen behandelt. Ein guter Ansatz besteht darin, die ersten Beispiele und Konzepte zu verstehen und dann zu den späteren Beispielen zu wechseln, die auf Ihren spezifischen Anforderungen basieren. Basierend auf diesem Ansatz beginnen wir mit einfachen "erforderlichen" und "optionalen" 1:1-Beziehungen.

Tipp

Der Code für alle unten aufgeführten Beispiele finden Sie in OneToOne.cs.

Erforderlich: 1:1

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

Eine 1:1-Beziehung besteht aus:

Tipp

Es ist nicht immer offensichtlich, welche Seite einer 1:1-Beziehung der Prinzipal sein sollte und welche Seite abhängig sein sollte. Einige Überlegungen sind:

  • Wenn die Datenbanktabellen für die beiden Typen bereits vorhanden sind, muss die Tabelle mit den Fremdschlüsselspalten dem abhängigen Typ zugeordnet werden.
  • Ein Typ ist in der Regel der abhängige Typ, wenn er ohne den anderen Typ nicht logisch vorhanden ist. Beispielsweise ist es nicht sinnvoll, eine Kopfzeile für einen Blog, der nicht existiert, zu haben, also BlogHeader ist natürlich der abhängige Typ.
  • Wenn es eine natürliche Eltern-Kind-Beziehung gibt, wird das Kind in der Regel als abhängig betrachtet.

Für die Beziehung in diesem Beispiel:

  • Die Fremdschlüsseleigenschaft BlogHeader.BlogId kann nicht null sein. Dies macht die Beziehung "erforderlich", da jeder Abhängige (BlogHeader) mit einer Hauptentität (Blog) verknüpft sein muss, da seine Fremdschlüsseleigenschaft auf einen bestimmten Wert festgelegt werden muss.
  • Beide Entitäten verfügen über Navigationen zur zugehörigen Entität auf der anderen Seite der Beziehung.

Hinweis

Eine erforderliche Beziehung stellt sicher, dass jede abhängige Entität mit einer Prinzipalentität verknüpft sein muss. Eine Prinzipalentität kann jedoch immer ohne abhängige Entität vorhanden sein. Das heißt, eine erforderliche Beziehung gibt nicht an, dass immer eine abhängige Entität vorhanden ist. Es gibt keine Möglichkeit im EF-Modell und auch keine Standardmethode in einer relationalen Datenbank, um sicherzustellen, dass ein Prinzipal einem abhängigen Zugeordnet ist. Wenn dies erforderlich ist, muss sie in der Anwendungslogik (Geschäftslogik) implementiert werden. Weitere Informationen finden Sie unter "Erforderliche Navigationen ".

Tipp

Eine Beziehung mit zwei Navigationen - eine von abhängig zum Prinzipal und eine Umkehrung von Prinzipal zu abhängig - wird als bidirektionale Beziehung bezeichnet.

Diese Beziehung wird durch Konventionen ermittelt. Dies bedeutet:

  • Blog wird als Prinzipal in der Beziehung ermittelt und BlogHeader als abhängig identifiziert.
  • BlogHeader.BlogId wird als Fremdschlüssel des abhängigen Objekts erkannt, der auf den Blog.Id Primärschlüssel des Hauptobjekts verweist. Die Beziehung wird als erforderlich erkannt, da BlogHeader.BlogId keine Nullwerte zulässig sind.
  • Blog.BlogHeader wird als Referenznavigation entdeckt.
  • BlogHeader.Blog wird als Referenznavigation entdeckt.

Von Bedeutung

Bei Verwendung von C#-Nullable-Bezugstypen muss die Navigation von der abhängigen Entität zur Prinzipalentität nullfähig sein, wenn die Fremdschlüsseleigenschaft nullfähig ist. Wenn die Fremdschlüsseleigenschaft nicht nullfähig ist, dann kann die Navigation nullfähig sein oder nicht. In diesem Fall sind sowohl BlogHeader.BlogId als auch BlogHeader.Blog nicht nullfähig. Das = null!; Konstrukt wird verwendet, um dies als beabsichtigt für den C#-Compiler zu markieren, da EF normalerweise die Blog Instanz festlegt und nicht null für eine vollständig geladene Beziehung sein kann. Weitere Informationen finden Sie unter Arbeiten mit Nullablen-Verweistypen .

In Fällen, in denen die Navigationen, Fremdschlüssel oder die erforderliche/optionale Art der Beziehung nicht durch Konvention ermittelt werden, können diese Dinge explizit konfiguriert werden. Beispiel:

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

Im obigen Beispiel beginnt die Konfiguration der Beziehungen mit dem Prinzipalentitätstyp (Blog). Wie bei allen Beziehungen ist es ebenso möglich, stattdessen mit dem abhängigen Entitätstyp (BlogHeader) zu beginnen. Beispiel:

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

Keine dieser Optionen ist besser als die andere; beide führen zu exakt derselben Konfiguration.

Tipp

Es ist nie erforderlich, eine Beziehung zweimal zu konfigurieren, einmal vom Prinzipal ausgehend, und dann erneut von der abhängigen. Außerdem funktioniert der Versuch, die Haupt- und die abhängigen Teile einer Beziehung separat zu konfigurieren, im Allgemeinen nicht. Wählen Sie aus, ob jede Beziehung von einem Ende oder dem anderen konfiguriert werden soll, und schreiben Sie dann den Konfigurationscode nur einmal.

Optionales Eins-zu-eins

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

Dies ist dasselbe wie das vorherige Beispiel, mit der Ausnahme, dass die Fremdschlüsseleigenschaft und die Navigation zur Hauptentität jetzt nullbar sind. Dies macht die Beziehung "optional", da ein abhängiges Objekt (BlogHeader) nicht mit einer Hauptentität (Blog) verknüpft werden kann, indem seine Fremdschlüsseleigenschaft und Navigation auf null gesetzt werden.

Von Bedeutung

Bei Verwendung von C# null-fähigen Bezugstypen muss die Navigationseigenschaft vom abhängigen zum prinzipalen Element nullfähig sein, wenn die Fremdschlüsseleigenschaft nullfähig ist. In diesem Fall kann BlogHeader.BlogId auf null gesetzt werden, daher muss BlogHeader.Blog auch auf null gesetzt werden können. Weitere Informationen finden Sie unter Arbeiten mit Nullablen-Verweistypen .

Wie zuvor wird diese Beziehung durch Konventionen ermittelt. In Fällen, in denen die Navigationen, Fremdschlüssel oder die erforderliche/optionale Art der Beziehung nicht durch Konvention ermittelt werden, können diese Dinge explizit konfiguriert werden. Beispiel:

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

Erforderliche Eins-zu-eins-Beziehung von Primärschlüssel zu Primärschlüssel

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

Im Gegensatz zu 1:n-Beziehungen kann das abhängige Ende einer 1:1-Beziehung die Primärschlüsseleigenschaft oder -eigenschaften als Fremdschlüsseleigenschaft oder -eigenschaften verwenden. Dies wird häufig als PK-to-PK-Beziehung bezeichnet. Dies ist nur möglich, wenn der Prinzipal- und der abhängige Typ dieselben Primärschlüsseltypen aufweisen und die resultierende Beziehung immer erforderlich ist, da der Primärschlüssel des abhängigen Schlüssels nicht nullfähig sein kann.

Jede Eins-zu-Eins-Beziehung, bei der der Fremdschlüssel nicht durch Konvention erkannt wird, muss so konfiguriert werden, dass die Haupt- und die abhängigen Enden der Beziehung angegeben werden. Dies erfolgt in der Regel mit einem Anruf an HasForeignKey. Beispiel:

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

Tipp

HasPrincipalKey kann auch für diesen Zweck verwendet werden, aber dies ist weniger häufig.

Wenn im Aufruf HasForeignKeykeine Eigenschaft angegeben wird und der Primärschlüssel geeignet ist, wird sie als Fremdschlüssel verwendet. In Fällen, in denen die Navigationen, Fremdschlüssel oder die erforderliche/optionale Art der Beziehung nicht durch Konvention ermittelt werden, können diese Dinge explizit konfiguriert werden. Beispiel:

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

Erforderlicher Eins-zu-Eins-Schlüssel mit verdecktem Fremdschlüssel

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

In einigen Fällen möchten Sie möglicherweise keine Fremdschlüsseleigenschaft in Ihrem Modell, da Fremdschlüssel ein Detail der Darstellung der Beziehung in der Datenbank sind, die bei der Verwendung der Beziehung auf rein objektorientierte Weise nicht erforderlich ist. Wenn Entitäten jedoch serialisiert werden, z. B. zum Senden über einen Draht, können die Fremdschlüsselwerte hilfreich sein, um die Beziehungsinformationen intakt zu halten, wenn sich die Entitäten nicht in einem Objektformular befinden. Daher ist es häufig pragmatisch, fremdschlüsseleigenschaften im .NET-Typ zu diesem Zweck beizubehalten. Fremdschlüsseleigenschaften können privat sein, was häufig ein guter Kompromiss ist, um zu vermeiden, dass der Fremdschlüssel offengelegt wird, während sein Wert mit der Entität übertragen wird.

Im folgenden Beispiel wird die Fremdschlüsseleigenschaft aus dem abhängigen Entitätstyp entfernt. Statt den Primärschlüssel zu verwenden, wird EF angewiesen, eine Schattenfremdschlüsseleigenschaft vom Typ BlogId mit dem Namen int zu erstellen.

Ein wichtiger Hinweis hier ist, dass C#-nullable Referenztypen verwendet werden, sodass die Nullfähigkeit der Navigation von der abhängigen Entität zur Hauptentität verwendet wird, um zu bestimmen, ob die Fremdschlüsseleigenschaft nullfähig ist und damit, ob die Beziehung optional oder verpflichtend ist. Wenn Nullable-Verweistypen nicht verwendet werden, wird die Schatten-Fremdschlüsseleigenschaft standardmäßig nullbar sein, sodass die Beziehung standardmäßig optional ist. Verwenden Sie IsRequired in diesem Fall, um zu erzwingen, dass die Schattenfremdschlüsseleigenschaft nicht-nullfähig ist und um die Beziehung erforderlich zu machen.

Diese Beziehung benötigt erneut eine Konfiguration, um den Prinzipal und die abhängigen Enden anzugeben:

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

In Fällen, in denen die Navigationen, Fremdschlüssel oder die erforderliche/optionale Art der Beziehung nicht durch Konvention ermittelt werden, können diese Dinge explizit konfiguriert werden. Beispiel:

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

Optionaler Eins-zu-eins-Schlüssel mit Schatten-Fremdschlüssel

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

Wie im vorherigen Beispiel wurde die Fremdschlüsseleigenschaft aus dem abhängigen Entitätstyp entfernt. Im Gegensatz zum vorherigen Beispiel wird die Fremdschlüsseleigenschaft jedoch als nullfähig erstellt, da C# nullable Verweistypen verwendet werden und die Navigation für den abhängigen Entitätstyp nullfähig ist. Dadurch wird die Beziehung optional.

Wenn C# nullable Referenztypen nicht verwendet werden, wird die Fremdschlüsseleigenschaft standardmäßig als nullable erstellt. Dies bedeutet, dass Beziehungen mit automatisch erstellten Schatteneigenschaften standardmäßig optional sind.

Wie zuvor benötigt diese Beziehung eine Konfiguration, um den Prinzipal und die abhängigen Enden anzugeben:

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

In Fällen, in denen die Navigationen, Fremdschlüssel oder die erforderliche/optionale Art der Beziehung nicht durch Konvention ermittelt werden, können diese Dinge explizit konfiguriert werden. Beispiel:

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

Eins-zu-eins ohne Navigation zum Hauptziel

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

In diesem Beispiel wurde die Fremdschlüsseleigenschaft neu eingeführt, die Navigation auf dem abhängigen Element wurde jedoch entfernt.

Tipp

Eine Beziehung mit nur einer Navigation – entweder vom Abhängigen zum Prinzipal oder vom Prinzipal zum Abhängigen, aber nicht in beide Richtungen –, wird als unidirektionale Beziehung bezeichnet.

Diese Beziehung wird durch Konventionen ermittelt, da der Fremdschlüssel ermittelt wird, wodurch die abhängige Seite angegeben wird. In Fällen, in denen die Navigationen, Fremdschlüssel oder die erforderliche/optionale Art der Beziehung nicht durch Konvention ermittelt werden, können diese Dinge explizit konfiguriert werden. Beispiel:

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

Beachten Sie, dass der Aufruf WithOne keine Argumente enthält. So teilt man EF mit, dass es keine Navigation von BlogHeader zu Blog gibt.

Wenn die Konfiguration mit der Entität ohne Navigation beginnt, muss der Typ der Entität am anderen Ende der Beziehung explizit mithilfe des generischen HasOne<>() Aufrufs angegeben werden. Beispiel:

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

Eins-zu-eins ohne Navigation zur Hauptentität und mit Schattenfremdschlüssel

// 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; }
}

In diesem Beispiel werden zwei der vorherigen Beispiele kombiniert, indem sowohl die Fremdschlüsseleigenschaft als auch die Navigation auf der abhängigen Entität entfernt werden.

Wie zuvor benötigt diese Beziehung eine Konfiguration, um den Prinzipal und die abhängigen Enden anzugeben:

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

Eine umfassendere Konfiguration kann verwendet werden, um den Navigations- und Fremdschlüsselnamen explizit zu konfigurieren, mit einem entsprechenden Aufruf von IsRequired() oder IsRequired(false) nach Bedarf. Beispiel:

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

Eins-zu-eins ohne Navigation zu abhängigen Elementen

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

In den beiden vorherigen Beispielen gab es Navigationen vom Prinzipal zu den Abhängigen, aber keine Navigation von den Abhängigen zum Prinzipal. Für die nächsten Beispiele wird die Navigation auf der abhängigen Instanz erneut eingeführt, während die Navigation auf der Hauptseite entfernt wird.

Üblicherweise behandelt EF dies als eine Eins-zu-Viele-Beziehung. Es ist eine minimale Konfiguration erforderlich, um eine 1:1-Verbindung herzustellen.

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

Beachten Sie erneut, dass WithOne() ohne Argumente aufgerufen wird, um anzugeben, dass in dieser Richtung keine Navigation vorhanden ist.

In Fällen, in denen die Navigationen, Fremdschlüssel oder die erforderliche/optionale Art der Beziehung nicht durch Konvention ermittelt werden, können diese Dinge explizit konfiguriert werden. Beispiel:

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

Wenn die Konfiguration mit der Entität ohne Navigation beginnt, muss der Typ der Entität am anderen Ende der Beziehung explizit mithilfe des generischen HasOne<>() Aufrufs angegeben werden. Beispiel:

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

1:1 ohne Navigation

Gelegentlich kann es hilfreich sein, eine Beziehung ohne Navigation zu konfigurieren. Eine solche Beziehung kann nur bearbeitet werden, indem der Fremdschlüsselwert direkt geändert wird.

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

Diese Beziehung wird nicht konventionsbedingt ermittelt, da keine Navigationselemente vorhanden sind, die angeben, dass die beiden Typen miteinander verknüpft sind. Es kann explizit in OnModelCreating konfiguriert werden. Beispiel:

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

Bei dieser Konfiguration wird die BlogHeader.BlogId Eigenschaft weiterhin als Fremdschlüssel nach Konvention erkannt, und die Beziehung ist "erforderlich", da die Fremdschlüsseleigenschaft nicht nullfähig ist. Die Beziehung kann "optional" gemacht werden, indem die Fremdschlüsseleigenschaft auf Nullable gesetzt wird.

Eine vollständigere explizite Konfiguration dieser Beziehung lautet:

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

1:1 mit alternativem Schlüssel

In allen bisherigen Beispielen wird die Fremdschlüsseleigenschaft des abhängigen Objekts auf den Primärschlüssel der Hauptentität beschränkt. Der Fremdschlüssel kann stattdessen auf eine andere Eigenschaft beschränkt werden, was dann zu einem alternativen Schlüssel für den Prinzipalentitätstyp wird. Beispiel:

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

Diese Beziehung wird nicht gemäß der Konvention ermittelt, da EF immer nach Konvention eine Beziehung zum Primärschlüssel erstellen wird. Sie kann explizit durch einen Aufruf in OnModelCreating konfiguriert HasPrincipalKey werden. Beispiel:

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

HasPrincipalKey kann mit anderen Aufrufen kombiniert werden, um die Navigationen, Fremdschlüsseleigenschaften und den obligatorischen/optionalen Charakter explizit zu konfigurieren. Beispiel:

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();
}

1:1 mit zusammengesetztem Fremdschlüssel

In allen Beispielen umfasste die Primär- oder alternative Schlüsseleigenschaft der Hauptentität nur eine einzige Eigenschaft. Primär- oder Alternativschlüssel können auch aus mehr als einer Eigenschaft gebildet werden, diese werden als "zusammengesetzte Schlüssel" bezeichnet. Wenn der Prinzipal einer Beziehung einen zusammengesetzten Schlüssel aufweist, muss der Fremdschlüssel des abhängigen Schlüssels auch ein zusammengesetzter Schlüssel mit derselben Anzahl von Eigenschaften sein. Beispiel:

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

Diese Beziehung wird durch Konventionen ermittelt. Es wird jedoch nur ermittelt, wenn der zusammengesetzte Schlüssel explizit konfiguriert wurde, da zusammengesetzte Schlüssel nicht automatisch ermittelt werden. Beispiel:

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

Von Bedeutung

Ein zusammengesetzter Fremdschlüsselwert wird als null betrachtet, wenn einer seiner Eigenschaftswerte den Wert null hat. Ein zusammengesetzter Fremdschlüssel mit einer Eigenschaft NULL und einem anderen Nicht-Null-Schlüssel wird nicht als Übereinstimmung für einen primären oder alternativen Schlüssel mit denselben Werten betrachtet. Beide werden berücksichtigt null.

Beide HasForeignKey und HasPrincipalKey können verwendet werden, um Schlüssel mit mehreren Eigenschaften explizit anzugeben. Beispiel:

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();
        });
}

Tipp

Im obigen Code sind die Aufrufe von HasKey und HasOne zu einem geschachtelten Builder gruppiert worden. Geschachtelte Builder beseitigen die Notwendigkeit, Entity<>() mehrmals für denselben Entitätstyp aufzurufen, sind jedoch funktional mit dem mehrfachen Aufrufen von Entity<>() vergleichbar.

Erforderliche 1:1-Beziehung ohne Kaskadenlöschung

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

In der Regel sind notwendige Beziehungen so konfiguriert, dass sie den Löschvorgang weitergeben. Dies liegt daran, dass der Abhängige nicht in der Datenbank existieren kann, sobald die Hauptentität gelöscht wurde. Die Datenbank kann so konfiguriert werden, dass ein Fehler generiert wird, was in der Regel dazu führt, dass die Anwendung abstürzt, anstatt die abhängigen Zeilen, die zukünftig nicht mehr existieren dürfen, automatisch zu löschen. Dies erfordert eine Konfiguration:

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

Selbstreferenzierende Eins-zu-eins-Beziehung

In allen vorherigen Beispielen unterscheidet sich der Prinzipalentitätstyp vom abhängigen Entitätstyp. Dies muss nicht der Fall sein. In den folgenden Typen ist jeder Person optional mit einem anderen Personverknüpft.

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
}

Diese Beziehung wird durch Konventionen ermittelt. In Fällen, in denen die Navigationen, Fremdschlüssel oder die erforderliche/optionale Art der Beziehung nicht durch Konvention ermittelt werden, können diese Dinge explizit konfiguriert werden. Beispiel:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Person>()
        .HasOne(e => e.Husband)
        .WithOne(e => e.Wife)
        .HasForeignKey<Person>(e => e.HusbandId)
        .IsRequired(false);
}

Hinweis

Bei eins-zu-eins selbstreferenzierenden Beziehungen, da die Haupt- und abhängigen Entitätstypen identisch sind, wird durch das Festlegen, welcher Typ den Fremdschlüssel enthält, das abhängige Element nicht deutlich gemacht. In diesem Fall zeigt die in HasOne angegebene Navigation vom abhängigen Objekt zum Hauptobjekt, und die in WithOne angegebene Navigation zeigt vom Hauptobjekt zum abhängigen Objekt.