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ärschlüssel oder einen alternativen Schlüssel auf der Hauptseite verweist. Aus Gründen der Einfachheit wird dieser Primär- oder Alternativschlüssel als "Prinzipalschlüssel" für die Beziehung bezeichnet. M:n-Beziehungen bestehen aus zwei 1:n-Beziehungen, von denen jede durch einen Fremdschlüssel definiert ist, 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. Zum Beispiel für einen Fremdschlüssel, der aus nur einer 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 als Zeichenfolge der Name der Fremdschlüsseleigenschaft übergeben werden. Zum Beispiel, 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 Eigenschaft oder 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.
Nicht-nullfähige 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 Ergebnis ist, dass die Fremdschlüsselspalte in der Datenbank nicht nullfähig ist, auch wenn das Fremdschlüsselattribut nullfähig ist. 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();
}
Schatten-Fremdschlüssel
Fremdschlüsseleigenschaften können als Schatteneigenschaften erstellt werden. Eine Schatteneigenschaft ist im EF-Modell vorhanden, ist jedoch nicht im .NET-Typ vorhanden. EF verfolgt den Eigenschaftswert und den Zustand intern.
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-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.
Schatten-Fremdschlüsseleigenschaften werden häufig durch Konventionen erstellt. Ein Schatten-Fremdschlüssel wird auch erstellt, wenn das Argument 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");
}
Üblicherweise erhält ein Schattenfremdschlüssel seinen Typ vom Primärschlüssel in der Beziehung. Dieser Typ kann nullwertet werden, es sei denn, die Beziehung wird als erforderlich erkannt oder konfiguriert.
Die Schatten-Fremdschlüsseleigenschaft kann auch explizit erstellt werden, was zum Konfigurieren von Facets der Eigenschaft hilfreich ist. Um die Eigenschaft beispielsweise nicht-nullbar zu machen:
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. Daher ist es selten notwendig, Facets 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 von Fremdschlüsseleinschränkungen
Fremdschlüsselbeschränkungen werden nach Konvention benannt FK_<dependent type name>_<principal type name>_<foreign key property name>
. 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.
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 Situationen sollten die Fremdschlüsselspalten normalen Eigenschaften zugeordnet werden, und diese Eigenschaften können dann manuell angepasst werden, um Änderungen an der Beziehung zu berücksichtigen.
Hauptschlü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. Zum Beispiel bei einem Fremdschlüssel für eine einzelne 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. Beispiel: 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
den alternativen Schlüssel für die Haupteinheit zu definieren; dies erfolgt automatisch, wenn HasPrincipalKey
Eigenschaften verwendet werden, die nicht die Eigenschaften des Primärschlüssels sind. 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 Nr. 30331.
Fremdschlüssel in viele-zu-viele-Beziehungen
In m:n-Beziehungen werden die Fremdschlüssel für den Verknüpfungsentitätstyp definiert und Fremdschlüsseleinschränkungen in der Verknüpfungstabelle 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"));
}