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.
Beim Arbeiten mit Entity Framework Code First besteht das Standardverhalten darin, Ihre POCO-Klassen Tabellen mithilfe einer Reihe von Konventionen zuzuordnen, die in EF integriert sind. Manchmal können sie diese Konventionen jedoch nicht einhalten und müssen Entitäten etwas anderes zuordnen als das, was die Konventionen diktieren.
Es gibt zwei Hauptmethoden, mit denen Sie EF so konfigurieren können, dass etwas anderes als Konventionen verwendet wird, nämlich Anmerkungen oder EFs fluent API. Die Anmerkungen decken nur eine Teilmenge der Fluent-API-Funktionalität ab, daher gibt es Zuordnungsszenarien, die nicht mithilfe von Anmerkungen erreicht werden können. Dieser Artikel wurde entwickelt, um zu veranschaulichen, wie die Fluent-API zum Konfigurieren von Eigenschaften verwendet wird.
Auf die Code First Fluent-API wird am häufigsten zugegriffen, indem die OnModelCreating-Methode für den abgeleiteten DbContext überschrieben wird. In den folgenden Beispielen wird gezeigt, wie verschiedene Aufgaben mit der Fluent-API ausgeführt werden können, und Sie können den Code kopieren und an Ihr Modell anpassen, wenn Sie das Modell sehen möchten, das mit as-is verwendet werden kann, dann wird es am Ende dieses Artikels bereitgestellt.
Modellweite Einstellungen
Standardschema (ab EF6 und später)
Ab EF6 können Sie die HasDefaultSchema-Methode auf DbModelBuilder verwenden, um das Datenbankschema anzugeben, das für alle Tabellen, gespeicherten Prozeduren usw. verwendet werden soll. Diese Standardeinstellung wird für alle Objekte außer Kraft gesetzt, für die Sie explizit ein anderes Schema konfigurieren.
modelBuilder.HasDefaultSchema("sales");
Benutzerdefinierte Konventionen (seit EF6)
Ab EF6 können Sie eigene Konventionen erstellen, um die in Code First enthaltenen Konventionen zu ergänzen. Weitere Informationen finden Sie unter "Erste Konventionen für benutzerdefinierten Code".
Eigenschaftszuordnung
Die Property-Methode wird verwendet, um Attribute für jede Eigenschaft zu konfigurieren, die zu einer Entität oder einem komplexen Typ gehört. Die Property-Methode wird verwendet, um ein Konfigurationsobjekt für eine bestimmte Eigenschaft abzurufen. Die Optionen für das Konfigurationsobjekt sind spezifisch für den typ, der konfiguriert wird; IsUnicode ist beispielsweise nur für Zeichenfolgeneigenschaften verfügbar.
Konfigurieren eines Primärschlüssels
Die Entity Framework-Konvention für Primärschlüssel lautet:
- Ihre Klasse definiert eine Eigenschaft, deren Name "ID" oder "ID" lautet.
- oder ein Klassen-Name gefolgt von "ID" oder "Id"
Um eine Eigenschaft explizit als Primärschlüssel festzulegen, können Sie die HasKey-Methode verwenden. Im folgenden Beispiel wird die HasKey-Methode verwendet, um den InstructorID-Primärschlüssel für den OfficeAssignment-Typ zu konfigurieren.
modelBuilder.Entity<OfficeAssignment>().HasKey(t => t.InstructorID);
Konfigurieren eines zusammengesetzten Primärschlüssels
Im folgenden Beispiel werden die Eigenschaften DepartmentID und Name als zusammengesetzter Primärschlüssel des Abteilungstyps konfiguriert.
modelBuilder.Entity<Department>().HasKey(t => new { t.DepartmentID, t.Name });
Deaktivieren der Identität für numerische Primärschlüssel
Im folgenden Beispiel wird die DepartmentID-Eigenschaft auf System.ComponentModel.DataAnnotations.DatabaseGeneratedOption.None festgelegt, um anzugeben, dass der Wert nicht von der Datenbank generiert wird.
modelBuilder.Entity<Department>().Property(t => t.DepartmentID)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
Angeben der maximalen Länge für eine Eigenschaft
Im folgenden Beispiel sollte die Name-Eigenschaft nicht mehr als 50 Zeichen lang sein. Wenn Sie den Wert länger als 50 Zeichen festlegen, erhalten Sie eine DbEntityValidationException-Ausnahme . Wenn Code First eine Datenbank aus diesem Modell erstellt, wird auch die maximale Länge der Spalte "Name" auf 50 Zeichen festgelegt.
modelBuilder.Entity<Department>().Property(t => t.Name).HasMaxLength(50);
Konfigurieren der erforderlichen Eigenschaft
Im folgenden Beispiel ist die Name-Eigenschaft erforderlich. Wenn Sie den Namen nicht angeben, erhalten Sie eine DbEntityValidationException-Ausnahme. Wenn Code First eine Datenbank aus diesem Modell erstellt, ist die Spalte, die zum Speichern dieser Eigenschaft verwendet wird, in der Regel nicht nullfähig.
Hinweis
In einigen Fällen ist es möglicherweise nicht möglich, dass die Spalte in der Datenbank nicht nullfähig ist, obwohl die Eigenschaft erforderlich ist. Wenn Sie z. B. eine TPH-Vererbungsstrategie für mehrere Typen verwenden, werden sie in einer einzelnen Tabelle gespeichert. Wenn ein abgeleiteter Typ eine erforderliche Eigenschaft enthält, kann die Spalte nicht nullfähig gemacht werden, da nicht alle Typen in der Hierarchie diese Eigenschaft aufweisen.
modelBuilder.Entity<Department>().Property(t => t.Name).IsRequired();
Konfigurieren eines Indexes für eine oder mehrere Eigenschaften
Hinweis
Nur ab EF6.1 – Das Index-Attribut wurde in das Entity Framework 6.1 eingeführt. Wenn Sie eine frühere Version verwenden, gelten die Informationen in diesem Abschnitt nicht.
Das Erstellen von Indizes wird von der Fluent-API nicht nativ unterstützt, Aber Sie können die Unterstützung für IndexAttribute über die Fluent-API verwenden. Indexattribute werden verarbeitet, indem eine Modellanmerkung in das Modell eingeschlossen wird, das dann später in der Pipeline in einen Index umgewandelt wird. Sie können diese Anmerkungen manuell mithilfe der Fluent-API hinzufügen.
Am einfachsten können Sie dazu eine Instanz von IndexAttribute erstellen, die alle Einstellungen für den neuen Index enthält. Anschließend können Sie eine Instanz von IndexAnnotation erstellen, bei der es sich um einen EF-spezifischen Typ handelt, der die IndexAttribute-Einstellungen in eine Modellanmerkung konvertiert, die im EF-Modell gespeichert werden kann. Diese können dann an die HasColumnAnnotation-Methode der Fluent-API übergeben werden, wobei der Name Index für die Anmerkung angegeben wird.
modelBuilder
.Entity<Department>()
.Property(t => t.Name)
.HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute()));
Eine vollständige Liste der in IndexAttribute verfügbaren Einstellungen finden Sie im Abschnitt "Index " von Code First Data Annotations. Dazu gehört das Anpassen des Indexnamens, das Erstellen eindeutiger Indizes und das Erstellen von mehrspaltigen Indizes.
Sie können mehrere Indexanmerkungen für eine einzelne Eigenschaft angeben, indem Sie ein Array von IndexAttribute an den Konstruktor von IndexAnnotation übergeben.
modelBuilder
.Entity<Department>()
.Property(t => t.Name)
.HasColumnAnnotation(
"Index",
new IndexAnnotation(new[]
{
new IndexAttribute("Index1"),
new IndexAttribute("Index2") { IsUnique = true }
})));
Angeben, dass eine CLR-Eigenschaft nicht einer Spalte in der Datenbank zugeordnet werden soll
Das folgende Beispiel zeigt, wie Sie angeben, dass eine Eigenschaft eines CLR-Typs keiner Spalte in der Datenbank zugeordnet ist.
modelBuilder.Entity<Department>().Ignore(t => t.Budget);
Zuordnen einer CLR-Eigenschaft zu einer bestimmten Spalte in der Datenbank
Im folgenden Beispiel wird die Name-CLR-Eigenschaft zur Spalte "DepartmentName" der Datenbank zugeordnet.
modelBuilder.Entity<Department>()
.Property(t => t.Name)
.HasColumnName("DepartmentName");
Umbenennen eines Fremdschlüssels, der im Modell nicht definiert ist
Wenn Sie sich entscheiden, keinen Fremdschlüssel für einen CLR-Typ zu definieren, aber den Namen in der Datenbank angeben möchten, gehen Sie wie folgt vor:
modelBuilder.Entity<Course>()
.HasRequired(c => c.Department)
.WithMany(t => t.Courses)
.Map(m => m.MapKey("ChangedDepartmentID"));
Konfigurieren, ob eine String-Eigenschaft Unicode-Inhalte unterstützt
Standardmäßig sind Zeichenfolgen Unicode (nvarchar in SQL Server). Mit der IsUnicode-Methode können Sie angeben, dass eine Zeichenfolge vom Typ varchar sein soll.
modelBuilder.Entity<Department>()
.Property(t => t.Name)
.IsUnicode(false);
Konfigurieren des Datentyps einer Datenbankspalte
Die HasColumnType-Methode ermöglicht die Zuordnung zu unterschiedlichen Darstellungen desselben Basistyps. Wenn Sie diese Methode verwenden, können Sie zur Laufzeit keine Konvertierung der Daten durchführen. Beachten Sie, dass IsUnicode die bevorzugte Methode ist, um Spalten als varchar festzulegen, da es datenbankagnostisch ist.
modelBuilder.Entity<Department>()
.Property(p => p.Name)
.HasColumnType("varchar");
Konfigurieren von Eigenschaften für einen komplexen Typ
Es gibt zwei Möglichkeiten, skalare Eigenschaften für einen komplexen Typ zu konfigurieren.
Sie können die Eigenschaft auf ComplexTypeConfiguration aufrufen.
modelBuilder.ComplexType<Details>()
.Property(t => t.Location)
.HasMaxLength(20);
Sie können auch die Punktnotation verwenden, um auf eine Eigenschaft eines komplexen Typs zuzugreifen.
modelBuilder.Entity<OnsiteCourse>()
.Property(t => t.Details.Location)
.HasMaxLength(20);
Konfigurieren einer Eigenschaft, die als optimistisches Parallelitätstoken verwendet werden soll
Um anzugeben, dass eine Eigenschaft in einer Entität ein Parallelitätstoken darstellt, können Sie entweder das ConcurrencyCheck-Attribut oder die IsConcurrencyToken-Methode verwenden.
modelBuilder.Entity<OfficeAssignment>()
.Property(t => t.Timestamp)
.IsConcurrencyToken();
Sie können auch die IsRowVersion-Methode verwenden, um die Eigenschaft als Zeilenversion in der Datenbank zu konfigurieren. Wenn die Eigenschaft als Zeilenversion festgelegt wird, wird sie automatisch als optimistisches Parallelitätstoken konfiguriert.
modelBuilder.Entity<OfficeAssignment>()
.Property(t => t.Timestamp)
.IsRowVersion();
Typzuordnung
Angeben, dass eine Klasse ein komplexer Typ ist
Standardmäßig wird ein Typ, der keinen Primärschlüssel angegeben hat, als komplexer Typ behandelt. Es gibt einige Szenarien, in denen Code First keinen komplexen Typ erkennt (z. B. wenn Sie eine Eigenschaft namens "ID" haben, die aber nicht bedeutet, dass es sich um einen Primärschlüssel handelt). In solchen Fällen würden Sie die Fluent-API verwenden, um explizit anzugeben, dass ein Typ ein komplexer Typ ist.
modelBuilder.ComplexType<Details>();
Angeben, dass kein CLR-Entitätstyp einer Tabelle in der Datenbank zugeordnet werden soll
Das folgende Beispiel zeigt, wie Sie einen CLR-Typ ausschließen, der einer Tabelle in der Datenbank zugeordnet wird.
modelBuilder.Ignore<OnlineCourse>();
Zuordnen eines Entitätstyps zu einer bestimmten Tabelle in der Datenbank
Alle Eigenschaften der Abteilung werden Spalten in einer Tabelle namens t_ Abteilung zugeordnet.
modelBuilder.Entity<Department>()
.ToTable("t_Department");
Sie können auch den Schemanamen wie folgt angeben:
modelBuilder.Entity<Department>()
.ToTable("t_Department", "school");
Abbildung der Table-Per-Hierarchy-(TPH)-Vererbung
Im TPH-Zuordnungsszenario werden alle Typen in einer Vererbungshierarchie einer einzelnen Tabelle zugeordnet. Eine Diskriminatorspalte wird verwendet, um den Typ jeder Zeile zu identifizieren. Beim Erstellen des Modells mit Code First ist TPH die Standardstrategie für die Typen, die an der Vererbungshierarchie teilnehmen. Standardmäßig wird die Diskriminatorspalte der Tabelle mit dem Namen "Diskriminator" hinzugefügt, und der CLR-Typname jedes Typs in der Hierarchie wird für die Diskriminatorwerte verwendet. Sie können das Standardverhalten mithilfe der Fluent-API ändern.
modelBuilder.Entity<Course>()
.Map<Course>(m => m.Requires("Type").HasValue("Course"))
.Map<OnsiteCourse>(m => m.Requires("Type").HasValue("OnsiteCourse"));
Zuordnen der Table-Per-Type (TPT)-Vererbung
Im TPT-Zuordnungsszenario werden alle Typen einzelnen Tabellen zugeordnet. Eigenschaften, die ausschließlich zu einem Basistyp oder abgeleitetem Typ gehören, werden in einer Tabelle gespeichert, die diesem Typ zugeordnet ist. Tabellen, die abgeleiteten Typen zugeordnet sind, speichern auch einen Fremdschlüssel, der die abgeleitete Tabelle mit der Basistabelle verknüpft.
modelBuilder.Entity<Course>().ToTable("Course");
modelBuilder.Entity<OnsiteCourse>().ToTable("OnsiteCourse");
Zuordnung der Tabellen-pro-Konkrete-Klasse-Vererbung (TPC)
Im TPC-Zuordnungsszenario werden alle nicht abstrakten Typen in der Hierarchie einzelnen Tabellen zugeordnet. Die Tabellen, die den abgeleiteten Klassen zugeordnet sind, haben keine Beziehung zur Tabelle, die der Basisklasse in der Datenbank zugeordnet ist. Alle Eigenschaften einer Klasse, einschließlich geerbter Eigenschaften, werden Spalten der entsprechenden Tabelle zugeordnet.
Rufen Sie die MapInheritedProperties-Methode auf, um jeden abgeleiteten Typ zu konfigurieren. MapInheritedProperties ordnet alle Eigenschaften, die von der Basisklasse geerbt wurden, neuen Spalten in der Tabelle der abgeleiteten Klasse zu.
Hinweis
Beachten Sie, dass die Tabellen, die an der TPC-Vererbungshierarchie teilnehmen, keinen gemeinsamen Primärschlüssel haben. Beim Einfügen in Tabellen, die auf Unterklassen abgebildet sind, gibt es doppelte Entitätenschlüssel, wenn Sie datenbankgenerierte Werte mit demselben Identitätsstartwert haben. Um dieses Problem zu beheben, können Sie entweder einen anderen Anfangswert für jede Tabelle angeben oder die Identität für die Primärschlüsseleigenschaft deaktivieren. Identity ist der Standardwert für ganzzahlige Schlüsseleigenschaften beim Arbeiten mit Code First.
modelBuilder.Entity<Course>()
.Property(c => c.CourseID)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
modelBuilder.Entity<OnsiteCourse>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("OnsiteCourse");
});
modelBuilder.Entity<OnlineCourse>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("OnlineCourse");
});
Zuordnen von Eigenschaften eines Entitätstyps zu mehreren Tabellen in der Datenbank (Entitätsaufteilung)
Durch die Entitätsteilung können die Eigenschaften eines Entitätstyps auf mehrere Tabellen verteilt werden. Im folgenden Beispiel wird die Abteilungsentität in zwei Tabellen unterteilt: "Abteilung" und "DepartmentDetails". Die Entitätsteilung verwendet mehrere Aufrufe der Map-Methode, um einer bestimmten Tabelle eine Teilmenge von Eigenschaften zuzuordnen.
modelBuilder.Entity<Department>()
.Map(m =>
{
m.Properties(t => new { t.DepartmentID, t.Name });
m.ToTable("Department");
})
.Map(m =>
{
m.Properties(t => new { t.DepartmentID, t.Administrator, t.StartDate, t.Budget });
m.ToTable("DepartmentDetails");
});
Zuordnen mehrerer Entitätstypen zu einer Tabelle in der Datenbank (Tabellenaufteilung)
Im folgenden Beispiel werden zwei Entitätstypen zugeordnet, die einen Primärschlüssel gemeinsam mit einer Tabelle verwenden.
modelBuilder.Entity<OfficeAssignment>()
.HasKey(t => t.InstructorID);
modelBuilder.Entity<Instructor>()
.HasRequired(t => t.OfficeAssignment)
.WithRequiredPrincipal(t => t.Instructor);
modelBuilder.Entity<Instructor>().ToTable("Instructor");
modelBuilder.Entity<OfficeAssignment>().ToTable("Instructor");
Zuordnen eines Entitätstyps zum Einfügen/Aktualisieren/Löschen gespeicherter Prozeduren (EF6 ab)
Ab EF6 können Sie eine Entität zuordnen, um gespeicherte Prozeduren zum Einfügen von Aktualisierungen und Löschen zu verwenden. Weitere Informationen finden Sie unter Code First Insert/Update/Delete Stored Procedures.
In Beispielen verwendetes Modell
Das folgende Code First-Modell wird für die Beispiele auf dieser Seite verwendet.
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;
// add a reference to System.ComponentModel.DataAnnotations DLL
using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
using System;
public class SchoolEntities : DbContext
{
public DbSet<Course> Courses { get; set; }
public DbSet<Department> Departments { get; set; }
public DbSet<Instructor> Instructors { get; set; }
public DbSet<OfficeAssignment> OfficeAssignments { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Configure Code First to ignore PluralizingTableName convention
// If you keep this convention then the generated tables will have pluralized names.
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
public class Department
{
public Department()
{
this.Courses = new HashSet<Course>();
}
// Primary key
public int DepartmentID { get; set; }
public string Name { get; set; }
public decimal Budget { get; set; }
public System.DateTime StartDate { get; set; }
public int? Administrator { get; set; }
// Navigation property
public virtual ICollection<Course> Courses { get; private set; }
}
public class Course
{
public Course()
{
this.Instructors = new HashSet<Instructor>();
}
// Primary key
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
// Foreign key
public int DepartmentID { get; set; }
// Navigation properties
public virtual Department Department { get; set; }
public virtual ICollection<Instructor> Instructors { get; private set; }
}
public partial class OnlineCourse : Course
{
public string URL { get; set; }
}
public partial class OnsiteCourse : Course
{
public OnsiteCourse()
{
Details = new Details();
}
public Details Details { get; set; }
}
public class Details
{
public System.DateTime Time { get; set; }
public string Location { get; set; }
public string Days { get; set; }
}
public class Instructor
{
public Instructor()
{
this.Courses = new List<Course>();
}
// Primary key
public int InstructorID { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public System.DateTime HireDate { get; set; }
// Navigation properties
public virtual ICollection<Course> Courses { get; private set; }
}
public class OfficeAssignment
{
// Specifying InstructorID as a primary
[Key()]
public Int32 InstructorID { get; set; }
public string Location { get; set; }
// When Entity Framework sees Timestamp attribute
// it configures ConcurrencyCheck and DatabaseGeneratedPattern=Computed.
[Timestamp]
public Byte[] Timestamp { get; set; }
// Navigation property
public virtual Instructor Instructor { get; set; }
}