Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Alle 1:1- und 1:N-Beziehungen werden durch einen Fremdschlüssel auf der abhängigen Seite definiert, der auf einen Primär- oder Alternativschlüssel auf der Prinzipalseite verweist. Aus Gründen der Einfachheit wird dieser Primär- oder Alternativschlüssel als "Prinzipalschlüssel" für die Beziehung bezeichnet. M:N-Beziehungen setzen sich aus zwei 1:N-Beziehungen zusammen, die ihrerseits durch einen Fremdschlüssel definiert sind, der auf einen Prinzipalschlüssel verweist.
Tipp
Der folgende Code finden Sie in ForeignAndPrincipalKeys.cs.
Fremdschlüssel
Die Eigenschaft oder Eigenschaften, aus denen fremder Schlüssel besteht, werden häufig nach Konvention ermittelt. Die Eigenschaften können auch explizit mithilfe von Zuordnungsattributen oder mit HasForeignKey der Modellerstellungs-API konfiguriert werden.
HasForeignKey kann mit einem Lambda-Ausdruck verwendet werden. Beispiel für einen Fremdschlüssel, der aus einer einzelnen Eigenschaft besteht:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasMany(e => e.Posts)
.WithOne(e => e.Blog)
.HasForeignKey(e => e.ContainingBlogId);
}
Oder für einen zusammengesetzten Fremdschlüssel aus mehreren Eigenschaften:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasMany(e => e.Posts)
.WithOne(e => e.Blog)
.HasForeignKey(e => new { e.ContainingBlogId1, e.ContainingBlogId2 });
}
Tipp
Die Verwendung von Lambda-Ausdrücken in der Modellerstellungs-API stellt sicher, dass die Verwendung der Eigenschaft für die Codeanalyse und -umgestaltung verfügbar ist, und stellt auch den Eigenschaftentyp für die API bereit, um sie in weiteren verketteten Methoden zu verwenden.
HasForeignKey kann auch der Name der Fremdschlüsseleigenschaft als Zeichenfolge übergeben werden. Beispielsweise für eine einzelne Eigenschaft:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasMany(e => e.Posts)
.WithOne(e => e.Blog)
.HasForeignKey("ContainingBlogId");
}
Oder für einen zusammengesetzten Fremdschlüssel:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasMany(e => e.Posts)
.WithOne(e => e.Blog)
.HasForeignKey("ContainingBlogId1", "ContainingBlogId2");
}
Die Verwendung einer Zeichenfolge ist nützlich, wenn:
- Die Eigenschaften sind privat.
- Die Eigenschaft oder Eigenschaften sind für den Entitätstyp nicht vorhanden und sollten als Schatteneigenschaften erstellt werden.
- Der Eigenschaftenname wird basierend auf bestimmten Eingaben im Modellbauprozess berechnet oder erstellt.
Non-Nullable-Fremdschlüsselspalten
Wie in optionalen und erforderlichen Beziehungen beschrieben, bestimmt die Nullierbarkeit der Fremdschlüsseleigenschaft, ob eine Beziehung optional oder erforderlich ist. Eine nullable Fremdschlüsseleigenschaft kann jedoch für eine erforderliche Beziehung mithilfe des [Required] Attributs oder durch Aufrufen IsRequired der Modellbau-API verwendet werden. Beispiel:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasMany(e => e.Posts)
.WithOne(e => e.Blog)
.HasForeignKey(e => e.BlogId)
.IsRequired();
}
Oder, wenn der Fremdschlüssel durch Konvention entdeckt wird, kann IsRequired ohne einen Aufruf von HasForeignKey verwendet werden.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasMany(e => e.Posts)
.WithOne(e => e.Blog)
.IsRequired();
}
Das Endergebnis ist, dass die Fremdschlüsselspalte in der Datenbank als „Non-Nullable“ festgelegt wird, auch wenn die Fremdschlüsseleigenschaft Nullwerte zulässt. Dasselbe kann erreicht werden, indem die Fremdschlüsseleigenschaft selbst explizit entsprechend konfiguriert wird. Beispiel:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>()
.Property(e => e.BlogId)
.IsRequired();
}
Schattenfremdschlüssel
Fremdschlüsseleigenschaften können als Schatteneigenschaften erstellt werden. Im EF-Modell ist eine Schatteneigenschaft vorhanden, ist jedoch für den .NET Typ nicht vorhanden. EF verfolgt den Eigenschaftswert und -zustand intern nach.
Schattenfremdschlüssel werden in der Regel verwendet, wenn das relationale Konzept eines Fremdschlüssels im Domänenmodell, das vom Anwendungscode oder der Geschäftslogik verwendet wird, verborgen werden soll. Dieser Anwendungscode bearbeitet dann die Beziehung vollständig durch Navigationen.
Tipp
Wenn Entitäten serialisiert werden, z. B. zum Senden über einen Draht, können die Fremdschlüsselwerte eine nützliche Möglichkeit sein, die Beziehungsinformationen intakt zu halten, wenn sich die Entitäten nicht in einem Objekt-/Diagrammformular befinden. Daher ist es häufig pragmatisch, Fremdschlüsseleigenschaften im .NET-Typen zu diesem Zweck zu belassen. Fremdschlüsseleigenschaften können privat sein, was oft ein guter Kompromiss ist, um zu vermeiden, dass der Fremdschlüssel offengelegt wird, während sein Wert bei der Entität verbleiben kann.
Schatten-Fremdschlüsseleigenschaften werden häufig durch Konventionen erstellt. Ein Schatten-Fremdschlüssel wird auch erstellt, wenn das Argument für HasForeignKey keiner .NET-Eigenschaft entspricht. Beispiel:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasMany(e => e.Posts)
.WithOne(e => e.Blog)
.HasForeignKey("MyBlogId");
}
Gemäß Konvention erhält ein Schattenfremdschlüssel seinen Typ vom Prinzipalschlüssel der Beziehung. Dieser Typ wird als „Nullwerte zulassend“ festgelegt, es sei denn, die Beziehung wird als erforderlich erkannt oder entsprechend konfiguriert.
Die Schatten-Fremdschlüsseleigenschaft kann auch explizit erstellt werden, was zum Konfigurieren von Facets der Eigenschaft hilfreich ist. So kann z. B. die Eigenschaft als „Non-Nullable“ festgelegt werden
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>()
.Property<string>("MyBlogId")
.IsRequired();
modelBuilder.Entity<Blog>()
.HasMany(e => e.Posts)
.WithOne(e => e.Blog)
.HasForeignKey("MyBlogId");
}
Tipp
Fremdschlüsseleigenschaften erben üblicherweise Eigenschaften wie maximale Länge und Unicode-Unterstützung vom Hauptschlüssel der betreffenden Beziehung. Es ist daher selten notwendig, Facetten für eine Fremdschlüsseleigenschaft explizit zu konfigurieren.
Die Erstellung einer Schatteneigenschaft, wenn der angegebene Name keiner Eigenschaft des Entitätstyps entspricht, kann mithilfe von ConfigureWarnings deaktiviert werden. Beispiel:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.ConfigureWarnings(b => b.Throw(CoreEventId.ShadowPropertyCreated));
Namen für Fremdschlüsseleinschränkungen
Fremdschlüsseleinschränkungen werden gemäß Konvention mit FK_<dependent type name>_<principal type name>_<foreign key property name> bezeichnet. Bei zusammengesetzten Fremdschlüsseln wird <foreign key property name> zu einer Liste von Fremdschlüsseleigenschaftsnamen, die durch Unterstriche getrennt sind.
Dies kann in der Modellerstellungs-API mithilfe von HasConstraintName geändert werden. Beispiel:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasMany(e => e.Posts)
.WithOne(e => e.Blog)
.HasForeignKey(e => e.BlogId)
.HasConstraintName("My_BlogId_Constraint");
}
Tipp
Der Einschränkungsname wird von der EF Runtime nicht verwendet. Sie wird nur beim Erstellen eines Datenbankschemas mit EF Core-Migrationen verwendet.
Ausschließen von Fremdschlüsseleinschränkungen aus Migrationen
Hinweis
Dieses Feature wird in EF Core 11 eingeführt, das derzeit als Vorschauversion verfügbar ist.
Manchmal ist es nützlich, die Fremdschlüsselbeziehung im EF-Modell darzustellen, aber ohne die entsprechende Fremdschlüsseleinschränkung in der Datenbank zu erstellen. Dies kann bei älteren Datenbanken auftreten, in denen Einschränkungen nicht vorhanden sind, oder in Datensynchronisierungsszenarien, in denen die Reihenfolge des Einfügens verwandter Entitäten vorübergehend gegen Einschränkungen der referenziellen Integrität verstößt. Verwenden Sie ExcludeForeignKeyFromMigrations in diesen Fällen, um zu verhindern, dass EF die Fremdschlüsseleinschränkung in Migrationen generiert (und EnsureCreated):
modelBuilder.Entity<Blog>()
.HasMany(e => e.Posts)
.WithOne(e => e.Blog)
.HasForeignKey(e => e.BlogId)
.ExcludeForeignKeyFromMigrations();
Mit dieser Konfiguration erstellt EF keine Fremdschlüsseleinschränkung in der Datenbank, aber die Beziehung wird weiterhin im EF-Modell nachverfolgt und kann normalerweise zum Laden verwandter Daten, zur Änderungsnachverfolgung usw. verwendet werden. EF erstellt weiterhin einen Datenbankindex für die Fremdschlüsselspalte, da Indizes Abfragen beschleunigen, unabhängig davon, ob eine Einschränkung vorhanden ist.
Um dies für alle Fremdschlüssel im Modell anzuwenden (z. B. um alle Fremdschlüsseleinschränkungen global zu deaktivieren), können Sie in OnModelCreating über alle Fremdschlüssel iterieren.
foreach (var foreignKey in modelBuilder.Model.GetEntityTypes().SelectMany(e => e.GetForeignKeys()))
{
foreignKey.SetIsExcludedFromMigrations(true);
}
Indizes für Fremdschlüssel
Standardmäßig erstellt EF einen Datenbankindex für die Eigenschaft oder die Eigenschaften eines Fremdschlüssels. Weitere Informationen zu den typen von Indizes, die von der Konvention erstellt wurden, finden Sie unter Modellbaukonventionen .
Tipp
Beziehungen werden im EF-Modell zwischen Entitätstypen definiert, die in diesem Modell enthalten sind. Einige Beziehungen müssen möglicherweise auf einen Entitätstyp im Modell eines anderen Kontexts verweisen, z. B. bei Verwendung des BoundedContext-Musters. In diesen Fällen sollte(n) die Fremdschlüsselspalte(n) normalen Eigenschaften zugeordnet werden. Diese Eigenschaften können dann manuell bearbeitet werden, um Änderungen an der Beziehung zu verarbeiten.
Prinzipalschlüssel
Fremdschlüssel werden standardmäßig auf den Primärschlüssel am Prinzipalende der Beziehung beschränkt. Stattdessen kann stattdessen ein alternativer Schlüssel verwendet werden. Dies wird mithilfe HasPrincipalKey der Modellbau-API erreicht. Beispielsweise für einen Fremdschlüssel mit einzelner Eigenschaft:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasMany(e => e.Posts)
.WithOne(e => e.Blog)
.HasPrincipalKey(e => e.AlternateId);
}
Oder für einen zusammengesetzten Fremdschlüssel mit mehreren Eigenschaften:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasMany(e => e.Posts)
.WithOne(e => e.Blog)
.HasPrincipalKey(e => new { e.AlternateId1, e.AlternateId2 });
}
HasPrincipalKey kann auch den Namen der alternativen Schlüsseleigenschaft als Zeichenfolge erhalten. Beispielsweise für einen einzelnen Eigenschaftsschlüssel:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasMany(e => e.Posts)
.WithOne(e => e.Blog)
.HasPrincipalKey("AlternateId");
}
Oder für einen zusammengesetzten Schlüssel:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasMany(e => e.Posts)
.WithOne(e => e.Blog)
.HasPrincipalKey("AlternateId1", "AlternateId2");
}
Hinweis
Die Reihenfolge der Eigenschaften im Prinzipal- und Fremdschlüssel muss übereinstimmen. Dies ist auch die Reihenfolge, in der der Schlüssel im Datenbankschema definiert ist. Sie muss nicht mit der Reihenfolge der Eigenschaften im Entitätstyp oder den Spalten in der Tabelle übereinstimmen.
Es ist nicht erforderlich, HasAlternateKey aufzurufen, um den Alternativschlüssel für die Prinzipalentität zu definieren. Dies erfolgt automatisch, wenn HasPrincipalKey mit Eigenschaften verwendet wird, die nicht den Primärschlüsseleigenschaften entsprechen. Jedoch kann HasAlternateKey verwendet werden, um den alternativen Schlüssel weiter zu konfigurieren, z. B. den Namen der Datenbankeinschränkung festzulegen. Weitere Informationen finden Sie unter Schlüssel.
Beziehungen zu schlüssellosen Entitäten
Jede Beziehung muss über einen Fremdschlüssel verfügen, der auf einen Primärschlüssel oder alternativen Schlüssel verweist. Dies bedeutet, dass ein schlüsselloser Entitätstyp nicht als Prinzipalende einer Beziehung fungieren kann, da kein Prinzipalschlüssel für die Fremdschlüssel vorhanden ist, auf die verwiesen werden soll.
Tipp
Ein Entitätstyp kann keinen alternativen Schlüssel, aber keinen Primärschlüssel haben. In diesem Fall muss der alternative Schlüssel (oder einer der alternativen Schlüssel, falls mehrere vorhanden sind) an den Primärschlüssel heraufgestuft werden.
Schlüssellose Entitätstypen können jedoch weiterhin Fremdschlüssel definiert haben und somit als abhängiges Ende einer Beziehung fungieren. Betrachten Sie beispielsweise diese Typen, bei denen Tag kein Schlüssel vorhanden ist:
public class Tag
{
public string Text { get; set; } = null!;
public int PostId { get; set; }
public Post Post { get; set; } = null!;
}
public class Post
{
public int Id { get; set; }
}
Tag kann am abhängigen Ende der Beziehung konfiguriert werden:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Tag>()
.HasNoKey();
modelBuilder.Entity<Post>()
.HasMany<Tag>()
.WithOne(e => e.Post);
}
Hinweis
EF unterstützt keine Navigationselemente, die auf schlüssellose Entitätstypen verweisen. Siehe GitHub Problem #30331.
Fremdschlüssel in M:N-Beziehungen
In M:N-Beziehungen werden die Fremdschlüssel hinsichtlich des Typs der Joinentität definiert und zu Fremdschlüsseleinschränkungen in der Jointabelle zugeordnet. Alle oben beschriebenen Elemente können auch auf diese Fremdschlüssel der Verknüpfungsentität angewendet werden. Beispiel: Festlegen der Namen der Datenbankeinschränkung:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>()
.HasMany(e => e.Tags)
.WithMany(e => e.Posts)
.UsingEntity(
l => l.HasOne(typeof(Tag)).WithMany().HasConstraintName("TagForeignKey_Constraint"),
r => r.HasOne(typeof(Post)).WithMany().HasConstraintName("PostForeignKey_Constraint"));
}