Entity Framework - Fließende API
Fluent API ist eine erweiterte Methode zur Angabe der Modellkonfiguration, die alles abdeckt, was Datenanmerkungen tun können, zusätzlich zu einer erweiterten Konfiguration, die mit Datenanmerkungen nicht möglich ist. Datenanmerkungen und die fließende API können zusammen verwendet werden, aber Code First hat Vorrang vor der fließenden API> Datenanmerkungen> Standardkonventionen.
Fluent API ist eine weitere Möglichkeit, Ihre Domänenklassen zu konfigurieren.
Auf die Code First Fluent-API wird am häufigsten zugegriffen, indem die OnModelCreating-Methode in Ihrem abgeleiteten DbContext überschrieben wird.
Fluent API bietet mehr Funktionen für die Konfiguration als DataAnnotations. Fluent API unterstützt die folgenden Arten von Zuordnungen.
In diesem Kapitel werden wir mit dem einfachen Beispiel fortfahren, das Klassen für Schüler, Kurse und Einschreibungen sowie eine Kontextklasse mit dem Namen MyContext enthält, wie im folgenden Code gezeigt.
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EFCodeFirstDemo {
class Program {
static void Main(string[] args) {}
}
public enum Grade {
A, B, C, D, F
}
public class Enrollment {
public int EnrollmentID { get; set; }
public int CourseID { get; set; }
public int StudentID { get; set; }
public Grade? Grade { get; set; }
public virtual Course Course { get; set; }
public virtual Student Student { get; set; }
}
public class Student {
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
public class Course {
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
public class MyContext : DbContext {
public virtual DbSet<Course> Courses { get; set; }
public virtual DbSet<Enrollment> Enrollments { get; set; }
public virtual DbSet<Student> Students { get; set; }
}
}
Um auf die Fluent-API zugreifen zu können, müssen Sie die OnModelCreating-Methode in DbContext überschreiben. Schauen wir uns ein einfaches Beispiel an, in dem wir den Spaltennamen in der Schülertabelle von FirstMidName in FirstName umbenennen, wie im folgenden Code gezeigt.
public class MyContext : DbContext {
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
modelBuilder.Entity<Student>().Property(s ⇒ s.FirstMidName)
.HasColumnName("FirstName");}
public virtual DbSet<Course> Courses { get; set; }
public virtual DbSet<Enrollment> Enrollments { get; set; }
public virtual DbSet<Student> Students { get; set; }
}
DbModelBuilder wird verwendet, um CLR-Klassen einem Datenbankschema zuzuordnen. Es ist die Hauptklasse, auf der Sie alle Ihre Domänenklassen konfigurieren können. Dieser codezentrierte Ansatz zum Erstellen eines Entity Data Model (EDM) wird als Code First bezeichnet.
Fluent API bietet eine Reihe wichtiger Methoden zum Konfigurieren von Entitäten und deren Eigenschaften, um verschiedene Code First-Konventionen zu überschreiben. Nachfolgend einige davon.
Sr. Nr. | Methodenname & Beschreibung |
---|---|
1 | ComplexType<TComplexType> Registriert einen Typ als komplexen Typ im Modell und gibt ein Objekt zurück, mit dem der komplexe Typ konfiguriert werden kann. Diese Methode kann für denselben Typ mehrmals aufgerufen werden, um mehrere Konfigurationszeilen auszuführen. |
2 | Entity<TEntityType> Registriert einen Entitätstyp als Teil des Modells und gibt ein Objekt zurück, mit dem die Entität konfiguriert werden kann. Diese Methode kann mehrmals aufgerufen werden, damit dieselbe Entität mehrere Konfigurationszeilen ausführt. |
3 | HasKey<TKey> Konfiguriert die Primärschlüsseleigenschaft (en) für diesen Entitätstyp. |
4 | HasMany<TTargetEntity> Konfiguriert viele Beziehungen aus diesem Entitätstyp. |
5 | HasOptional<TTargetEntity> Konfiguriert eine optionale Beziehung aus diesem Entitätstyp. Instanzen des Entitätstyps können in der Datenbank gespeichert werden, ohne dass diese Beziehung angegeben wird. Der Fremdschlüssel in der Datenbank ist nullwertfähig. |
6 | HasRequired<TTargetEntity> Konfiguriert eine erforderliche Beziehung aus diesem Entitätstyp. Instanzen des Entitätstyps können nur in der Datenbank gespeichert werden, wenn diese Beziehung angegeben ist. Der Fremdschlüssel in der Datenbank ist nicht nullwertfähig. |
7 | Ignore<TProperty> Schließt eine Eigenschaft aus dem Modell aus, damit sie nicht der Datenbank zugeordnet wird. (Von StructuralTypeConfiguration <TStructuralType> geerbt) |
8 | Property<T> Konfiguriert eine struct-Eigenschaft, die für diesen Typ definiert ist. (Von StructuralTypeConfiguration <TStructuralType> geerbt) |
9 | ToTable(String) Konfiguriert den Tabellennamen, dem dieser Entitätstyp zugeordnet ist. |
Mit Fluent API können Sie Ihre Entitäten oder deren Eigenschaften konfigurieren, unabhängig davon, ob Sie etwas daran ändern möchten, wie sie der Datenbank zugeordnet sind oder in welcher Beziehung sie zueinander stehen. Es gibt eine Vielzahl von Zuordnungen und Modellen, die Sie mithilfe der Konfigurationen beeinflussen können. Im Folgenden sind die wichtigsten Arten der Zuordnung aufgeführt, die von Fluent API unterstützt werden:
- Entitätszuordnung
- Eigenschaftenzuordnung
Entitätszuordnung
Die Entitätszuordnung ist nur eine einfache Zuordnung, die sich auf das Verständnis von Entity Framework auswirkt, wie die Klassen den Datenbanken zugeordnet werden. All dies haben wir in Datenanmerkungen besprochen. Hier erfahren Sie, wie Sie mit der Fluent-API dasselbe erreichen.
Anstatt in die Domänenklassen zu gehen, um diese Konfigurationen hinzuzufügen, können wir dies innerhalb des Kontexts tun.
Als erstes müssen Sie die OnModelCreating-Methode überschreiben, mit der der modelBuilder arbeiten kann.
Standardschema
Das Standardschema ist dbo, wenn die Datenbank generiert wird. Sie können die HasDefaultSchema-Methode in DbModelBuilder verwenden, um das Datenbankschema anzugeben, das für alle Tabellen, gespeicherten Prozeduren usw. verwendet werden soll.
Schauen wir uns das folgende Beispiel an, in dem das Administratorschema angewendet wird.
public class MyContext : DbContext {
public MyContext() : base("name = MyContextDB") {}
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
//Configure default schema
modelBuilder.HasDefaultSchema("Admin");
}
public virtual DbSet<Course> Courses { get; set; }
public virtual DbSet<Enrollment> Enrollments { get; set; }
public virtual DbSet<Student> Students { get; set; }
}
Entität der Tabelle zuordnen
Mit der Standardkonvention erstellt Code First die Datenbanktabellen mit dem Namen der DbSet-Eigenschaften in der Kontextklasse, z. B. Kurse, Einschreibungen und Studenten. Wenn Sie jedoch andere Tabellennamen wünschen, können Sie diese Konvention überschreiben und einen anderen Tabellennamen als die DbSet-Eigenschaften angeben, wie im folgenden Code gezeigt.
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
//Configure default schema
modelBuilder.HasDefaultSchema("Admin");
//Map entity to table
modelBuilder.Entity<Student>().ToTable("StudentData");
modelBuilder.Entity<Course>().ToTable("CourseDetail");
modelBuilder.Entity<Enrollment>().ToTable("EnrollmentInfo");
}
Wenn die Datenbank generiert wird, wird der in der OnModelCreating-Methode angegebene Tabellenname angezeigt.
Aufteilen von Entitäten (Zuordnung von Entität zu mehreren Tabellen)
Mit der Entitätsaufteilung können Sie Daten aus mehreren Tabellen zu einer einzigen Klasse kombinieren. Sie können nur mit Tabellen verwendet werden, zwischen denen eine Eins-zu-Eins-Beziehung besteht. Schauen wir uns das folgende Beispiel an, in dem Schülerinformationen in zwei Tabellen abgebildet werden.
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
//Configure default schema
modelBuilder.HasDefaultSchema("Admin");
//Map entity to table
modelBuilder.Entity<Student>().Map(sd ⇒ {
sd.Properties(p ⇒ new { p.ID, p.FirstMidName, p.LastName });
sd.ToTable("StudentData");
})
.Map(si ⇒ {
si.Properties(p ⇒ new { p.ID, p.EnrollmentDate });
si.ToTable("StudentEnrollmentInfo");
});
modelBuilder.Entity<Course>().ToTable("CourseDetail");
modelBuilder.Entity<Enrollment>().ToTable("EnrollmentInfo");
}
Im obigen Code können Sie sehen, dass die Student-Entität in die folgenden zwei Tabellen aufgeteilt ist, indem Sie einige Eigenschaften der StudentData-Tabelle und einige Eigenschaften der StudentEnrollmentInfo-Tabelle mithilfe der Map-Methode zuordnen.
StudentData - Enthält den Vor- und Nachnamen des Schülers.
StudentEnrollmentInfo - Enthält das Registrierungsdatum.
Wenn die Datenbank generiert wird, sehen Sie die folgenden Tabellen in Ihrer Datenbank, wie in der folgenden Abbildung gezeigt.
Eigenschaftenzuordnung
Die Eigenschaftsmethode 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. Sie können die Eigenschaften Ihrer Domänenklassen auch mithilfe der Fluent-API zuordnen und konfigurieren.
Primärschlüssel konfigurieren
Die Standardkonvention für Primärschlüssel lautet -
- Klasse definiert eine Eigenschaft, deren Name "ID" oder "ID" ist.
- Klassenname gefolgt von "ID" oder "ID"
Wenn Ihre Klasse nicht den Standardkonventionen für den Primärschlüssel entspricht, wie im folgenden Code der Schülerklasse gezeigt -
public class Student {
public int StdntID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
Um eine Eigenschaft explizit als Primärschlüssel festzulegen, können Sie die HasKey-Methode verwenden, wie im folgenden Code gezeigt:
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
//Configure default schema
modelBuilder.HasDefaultSchema("Admin");
// Configure Primary Key
modelBuilder.Entity<Student>().HasKey<int>(s ⇒ s.StdntID);
}
Spalte konfigurieren
In Entity Framework erstellt Code First standardmäßig eine Spalte für eine Eigenschaft mit demselben Namen, derselben Reihenfolge und demselben Datentyp. Sie können diese Konvention jedoch auch überschreiben, wie im folgenden Code gezeigt.
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
//Configure default schema
modelBuilder.HasDefaultSchema("Admin");
//Configure EnrollmentDate Column
modelBuilder.Entity<Student>().Property(p ⇒ p.EnrollmentDate)
.HasColumnName("EnDate")
.HasColumnType("DateTime")
.HasColumnOrder(2);
}
Konfigurieren Sie die MaxLength-Eigenschaft
Im folgenden Beispiel sollte die Eigenschaft "Kurstitel" nicht länger als 24 Zeichen sein. Wenn der Benutzer einen Wert angibt, der länger als 24 Zeichen ist, erhält der Benutzer eine DbEntityValidationException-Ausnahme.
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
//Configure default schema
modelBuilder.HasDefaultSchema("Admin");
modelBuilder.Entity<Course>().Property(p ⇒ p.Title).HasMaxLength(24);
}
Konfigurieren Sie die Null- oder NotNull-Eigenschaft
Im folgenden Beispiel ist die Eigenschaft "Kurstitel" erforderlich, damit die Methode "IsRequired" zum Erstellen der NotNull-Spalte verwendet wird. In ähnlicher Weise ist Student EnrollmentDate optional, sodass wir die IsOptional-Methode verwenden, um einen Nullwert in dieser Spalte zuzulassen, wie im folgenden Code gezeigt.
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
//Configure default schema
modelBuilder.HasDefaultSchema("Admin");
modelBuilder.Entity<Course>().Property(p ⇒ p.Title).IsRequired();
modelBuilder.Entity<Student>().Property(p ⇒ p.EnrollmentDate).IsOptional();
//modelBuilder.Entity<Student>().Property(s ⇒ s.FirstMidName)
//.HasColumnName("FirstName");
}
Beziehungen konfigurieren
Eine Beziehung im Kontext von Datenbanken ist eine Situation, die zwischen zwei relationalen Datenbanktabellen besteht, wenn eine Tabelle einen Fremdschlüssel hat, der auf den Primärschlüssel der anderen Tabelle verweist. Wenn Sie mit Code First arbeiten, definieren Sie Ihr Modell, indem Sie Ihre Domänen-CLR-Klassen definieren. Standardmäßig verwendet das Entity Framework die Code First-Konventionen, um Ihre Klassen dem Datenbankschema zuzuordnen.
Wenn Sie die Namenskonventionen von Code First verwenden, können Sie sich in den meisten Fällen auf Code First verlassen, um Beziehungen zwischen Ihren Tabellen basierend auf den Fremdschlüsseln und Navigationseigenschaften einzurichten.
Wenn sie diese Konventionen nicht erfüllen, gibt es auch Konfigurationen, mit denen Sie die Beziehungen zwischen Klassen beeinflussen können und wie diese Beziehungen in der Datenbank realisiert werden, wenn Sie Konfigurationen in Code First hinzufügen.
Einige davon sind in den Datenanmerkungen verfügbar, und Sie können einige noch kompliziertere mit einer Fluent-API anwenden.
Konfigurieren Sie die Eins-zu-Eins-Beziehung
Wenn Sie in Ihrem Modell eine Eins-zu-Eins-Beziehung definieren, verwenden Sie in jeder Klasse eine Referenznavigationseigenschaft. In der Datenbank können beide Tabellen nur einen Datensatz auf beiden Seiten der Beziehung haben. Jeder Primärschlüsselwert bezieht sich nur auf einen Datensatz (oder keine Datensätze) in der zugehörigen Tabelle.
Eine Eins-zu-Eins-Beziehung wird erstellt, wenn beide zugehörigen Spalten Primärschlüssel sind oder eindeutige Einschränkungen aufweisen.
In einer Eins-zu-Eins-Beziehung fungiert der Primärschlüssel zusätzlich als Fremdschlüssel, und für beide Tabellen gibt es keine separate Fremdschlüsselspalte.
Diese Art von Beziehung ist nicht üblich, da sich die meisten auf diese Weise zusammenhängenden Informationen in einer Tabelle befinden.
Schauen wir uns das folgende Beispiel an, in dem wir unserem Modell eine weitere Klasse hinzufügen, um eine Eins-zu-Eins-Beziehung zu erstellen.
public class Student {
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public virtual StudentLogIn StudentLogIn { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
public class StudentLogIn {
[Key, ForeignKey("Student")]
public int ID { get; set; }
public string EmailID { get; set; }
public string Password { get; set; }
public virtual Student Student { get; set; }
}
Wie Sie im obigen Code sehen können, werden die Attribute Key und ForeignKey für die ID-Eigenschaft in der StudentLogIn-Klasse verwendet, um sie sowohl als Primärschlüssel als auch als Fremdschlüssel zu markieren.
Um eine Eins-zu-Null- oder eine Beziehung zwischen Student und StudentLogIn mithilfe der Fluent-API zu konfigurieren, müssen Sie die OnModelCreating-Methode wie im folgenden Code gezeigt überschreiben.
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
//Configure default schema
modelBuilder.HasDefaultSchema("Admin");
// Configure ID as PK for StudentLogIn
modelBuilder.Entity<StudentLogIn>()
.HasKey(s ⇒ s.ID);
// Configure ID as FK for StudentLogIn
modelBuilder.Entity<Student>()
.HasOptional(s ⇒ s.StudentLogIn) //StudentLogIn is optional
.WithRequired(t ⇒ t.Student); // Create inverse relationship
}
In den meisten Fällen kann das Entity Framework ableiten, welcher Typ der abhängige und welcher der Principal in einer Beziehung ist. Wenn jedoch beide Enden der Beziehung erforderlich sind oder beide Seiten optional sind, kann das Entity Framework den Abhängigen und den Auftraggeber nicht identifizieren. Wenn beide Enden der Beziehung erforderlich sind, können Sie HasRequired wie im folgenden Code gezeigt verwenden.
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
//Configure default schema
modelBuilder.HasDefaultSchema("Admin");
// Configure ID as PK for StudentLogIn
modelBuilder.Entity<StudentLogIn>()
.HasKey(s ⇒ s.ID);
// Configure ID as FK for StudentLogIn
modelBuilder.Entity<Student>()
.HasRequired(r ⇒ r.Student)
.WithOptional(s ⇒ s.StudentLogIn);
}
Wenn die Datenbank generiert wird, wird diese Beziehung wie in der folgenden Abbildung dargestellt erstellt.
Konfigurieren Sie die Eins-zu-Viele-Beziehung
Die Primärschlüsseltabelle enthält nur einen Datensatz, der sich auf keinen, einen oder mehrere Datensätze in der zugehörigen Tabelle bezieht. Dies ist die am häufigsten verwendete Art von Beziehung.
In dieser Art von Beziehung kann eine Zeile in Tabelle A viele übereinstimmende Zeilen in Tabelle B haben, aber eine Zeile in Tabelle B kann nur eine übereinstimmende Zeile in Tabelle A haben.
Der Fremdschlüssel wird in der Tabelle definiert, die das viele Ende der Beziehung darstellt.
In der obigen Abbildung haben Schüler- und Einschreibungstabellen beispielsweise eine Beziehung zwischen zwei Personen. Jeder Schüler kann viele Einschreibungen haben, aber jede Einschreibung gehört nur einem Schüler.
Nachfolgend finden Sie die Schüler- und Einschreibungsbeziehung mit einer Eins-zu-Viele-Beziehung, aber der Fremdschlüssel in der Einschreibungstabelle entspricht nicht den Standardkonventionen von Code First.
public class Enrollment {
public int EnrollmentID { get; set; }
public int CourseID { get; set; }
//StdntID is not following code first conventions name
public int StdntID { get; set; }
public Grade? Grade { get; set; }
public virtual Course Course { get; set; }
public virtual Student Student { get; set; }
}
public class Student {
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public virtual StudentLogIn StudentLogIn { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
In diesem Fall müssen Sie zum Konfigurieren der Eins-zu-Viele-Beziehung mithilfe der Fluent-API die HasForeignKey-Methode verwenden, wie im folgenden Code gezeigt.
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
//Configure default schema
modelBuilder.HasDefaultSchema("Admin");
//Configure FK for one-to-many relationship
modelBuilder.Entity<Enrollment>()
.HasRequired<Student>(s ⇒ s.Student)
.WithMany(t ⇒ t.Enrollments)
.HasForeignKey(u ⇒ u.StdntID);
}
Wenn die Datenbank generiert wird, sehen Sie, dass die Beziehung wie in der folgenden Abbildung gezeigt erstellt wird.
Im obigen Beispiel gibt die HasRequired-Methode an, dass die Student-Navigationseigenschaft Null sein muss. Daher müssen Sie Schüler jedes Mal, wenn Sie eine Registrierung hinzufügen oder aktualisieren, der Entität "Registrierung" zuweisen. Um dies zu handhaben, müssen wir die HasOptional-Methode anstelle der HasRequired-Methode verwenden.
Konfigurieren Sie die Viele-zu-Viele-Beziehung
Jeder Datensatz in beiden Tabellen kann sich auf eine beliebige Anzahl von Datensätzen (oder keine Datensätze) in der anderen Tabelle beziehen.
Sie können eine solche Beziehung erstellen, indem Sie eine dritte Tabelle definieren, die als Junction-Tabelle bezeichnet wird und deren Primärschlüssel aus den Fremdschlüsseln aus Tabelle A und Tabelle B besteht.
Beispielsweise haben die Schülertabelle und die Kurstabelle eine Viele-zu-Viele-Beziehung.
Im Folgenden sind die Klassen für Schüler und Kurse aufgeführt, in denen Schüler und Kurs viele Beziehungen haben, da beide Klassen über Navigationseigenschaften verfügen. Schüler und Kurse sind Sammlungen. Mit anderen Worten, eine Entität hat eine andere Entitätssammlung.
public class Student {
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public virtual ICollection<Course> Courses { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
public class Course {
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
public virtual ICollection<Student> Students { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
Um die Viele-zu-Viele-Beziehung zwischen Schüler und Kurs zu konfigurieren, können Sie die Fluent-API verwenden, wie im folgenden Code gezeigt.
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
//Configure default schema
modelBuilder.HasDefaultSchema("Admin");
// Configure many-to-many relationship
modelBuilder.Entity<Student>()
.HasMany(s ⇒ s.Courses)
.WithMany(s ⇒ s.Students);
}
Die Standardkonventionen von Code First werden verwendet, um beim Generieren einer Datenbank eine Verknüpfungstabelle zu erstellen. Infolgedessen wird die StudentCourses-Tabelle mit den Spalten Course_CourseID und Student_ID erstellt, wie in der folgenden Abbildung dargestellt.
Wenn Sie den Namen der Verknüpfungstabelle und die Namen der Spalten in der Tabelle angeben möchten, müssen Sie mithilfe der Map-Methode eine zusätzliche Konfiguration vornehmen.
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
//Configure default schema
modelBuilder.HasDefaultSchema("Admin");
// Configure many-to-many relationship
modelBuilder.Entity<Student>()
.HasMany(s ⇒ s.Courses)
.WithMany(s ⇒ s.Students)
.Map(m ⇒ {
m.ToTable("StudentCoursesTable");
m.MapLeftKey("StudentID");
m.MapRightKey("CourseID");
});
}
Sie können sehen, wann die Datenbank generiert wird. Der Name der Tabelle und der Spalten werden wie im obigen Code angegeben erstellt.
Wir empfehlen, dass Sie das obige Beispiel zum besseren Verständnis Schritt für Schritt ausführen.