Entity Framework - Kurzanleitung
Was ist Entity Framework?
Entity Framework wurde erstmals im Jahr 2008 veröffentlicht. Microsoft ist das wichtigste Mittel für die Interaktion zwischen .NET-Anwendungen und relationalen Datenbanken. Entity Framework ist ein Object Relational Mapper (ORM), ein Tool, das die Zuordnung zwischen Objekten in Ihrer Software zu den Tabellen und Spalten einer relationalen Datenbank vereinfacht.
Entity Framework (EF) ist ein Open Source ORM-Framework für ADO.NET, das Teil von .NET Framework ist.
Ein ORM kümmert sich darum, Datenbankverbindungen zu erstellen und Befehle auszuführen, Abfrageergebnisse zu erfassen und diese Ergebnisse automatisch als Anwendungsobjekte zu materialisieren.
Ein ORM hilft auch dabei, Änderungen an diesen Objekten zu verfolgen. Wenn Sie dazu aufgefordert werden, werden diese Änderungen auch für Sie in der Datenbank gespeichert.
Warum Entity Framework?
Entity Framework ist ein ORM, und ORMs zielen darauf ab, die Produktivität des Entwicklers zu steigern, indem die redundante Aufgabe reduziert wird, die in den Anwendungen verwendeten Daten beizubehalten.
Entity Framework kann die erforderlichen Datenbankbefehle zum Lesen oder Schreiben von Daten in die Datenbank generieren und für Sie ausführen.
Wenn Sie abfragen, können Sie Ihre Abfragen mit LINQ an Entitäten für Ihre Domänenobjekte ausdrücken.
Entity Framework führt die entsprechende Abfrage in der Datenbank aus und materialisiert die Ergebnisse in Instanzen Ihrer Domänenobjekte, damit Sie in Ihrer App arbeiten können.
Es gibt andere ORMs auf dem Markt wie NHibernate und LLBLGen Pro. Die meisten ORMs ordnen Domänentypen normalerweise direkt dem Datenbankschema zu.
Entity Framework verfügt über eine detailliertere Zuordnungsebene, sodass Sie Zuordnungen anpassen können, indem Sie beispielsweise die einzelne Entität mehreren Datenbanktabellen oder sogar mehrere Entitäten einer einzelnen Tabelle zuordnen.
Entity Framework ist die von Microsoft empfohlene Datenzugriffstechnologie für neue Anwendungen.
ADO.NET scheint sich direkt auf die Technologie für Datensätze und Datentabellen zu beziehen.
Im Entity Framework werden alle künftigen Investitionen getätigt, was bereits seit einigen Jahren der Fall ist.
Microsoft empfiehlt, dass Sie Entity Framework über ADO.NET oder LINQ to SQL für alle Neuentwicklungen verwenden.
Konzeptmodell
Für Entwickler, die an datenbankorientierte Entwicklung gewöhnt sind, besteht die größte Veränderung bei Entity Framework darin, dass Sie sich auf Ihre Geschäftsdomäne konzentrieren können. Was soll Ihre Anwendung tun, ohne durch die Leistungsfähigkeit der Datenbank eingeschränkt zu werden?
Bei Entity Framework wird der Schwerpunkt als konzeptionelles Modell bezeichnet. Es ist ein Modell der Objekte in Ihrer Anwendung, kein Modell der Datenbank, mit der Sie Ihre Anwendungsdaten beibehalten.
Ihr konzeptionelles Modell stimmt möglicherweise mit Ihrem Datenbankschema überein oder es kann ganz anders sein.
Mit einem Visual Designer können Sie Ihr konzeptionelles Modell definieren, das dann die Klassen generiert, die Sie letztendlich in Ihrer Anwendung verwenden werden.
Sie können einfach Ihre Klassen definieren und eine Funktion von Entity Framework namens Code First verwenden. Und dann wird Entity Framework das konzeptionelle Modell verstehen.
In beiden Fällen ermittelt Entity Framework, wie Sie von Ihrem konzeptionellen Modell zu Ihrer Datenbank wechseln können. Sie können also Ihre konzeptionellen Modellobjekte abfragen und direkt mit ihnen arbeiten.
Eigenschaften
Im Folgenden finden Sie die Grundfunktionen von Entity Framework. Diese Liste basiert auf den wichtigsten Funktionen und auch auf häufig gestellten Fragen zu Entity Framework.
- Entity Framework ist ein Microsoft-Tool.
- Entity Framework wird als Open Source-Produkt entwickelt.
- Entity Framework ist nicht mehr an den .NET-Veröffentlichungszyklus gebunden oder von diesem abhängig.
- Funktioniert mit jeder relationalen Datenbank mit einem gültigen Entity Framework-Anbieter.
- SQL-Befehlsgenerierung von LINQ zu Entities.
- Entity Framework erstellt parametrisierte Abfragen.
- Verfolgt Änderungen an In-Memory-Objekten.
- Ermöglicht das Einfügen, Aktualisieren und Löschen der Befehlsgenerierung.
- Arbeitet mit einem visuellen Modell oder mit Ihren eigenen Klassen.
- Entity Framework hat die Prozedurunterstützung gespeichert.
Die Architektur von Entity Framework besteht von unten nach oben aus:
Datenanbieter
Hierbei handelt es sich um quellenspezifische Anbieter, die die ADO.NET-Schnittstellen abstrahieren, um bei der Programmierung anhand des konzeptionellen Schemas eine Verbindung zur Datenbank herzustellen.
Es übersetzt die gängigen SQL-Sprachen wie LINQ über den Befehlsbaum in einen nativen SQL-Ausdruck und führt ihn für das jeweilige DBMS-System aus.
Entity Client
Diese Ebene macht die Entitätsebene der oberen Ebene zugänglich. Der Entitätsclient bietet Entwicklern die Möglichkeit, mithilfe von Entitäts-SQL-Abfragen mit Entitäten in Form von Zeilen und Spalten zu arbeiten, ohne dass Klassen zur Darstellung des konzeptionellen Schemas generiert werden müssen. Entity Client zeigt die Entity Framework-Ebenen an, die die Kernfunktionalität darstellen. Diese Ebenen werden als Entitätsdatenmodell bezeichnet.
Das Storage Layer enthält das gesamte Datenbankschema im XML-Format.
Das Entity Layer Dies ist auch eine XML-Datei, die die Entitäten und Beziehungen definiert.
Das Mapping layer ist eine XML-Datei, die die auf der konzeptionellen Ebene definierten Entitäten und Beziehungen mit den auf der logischen Ebene definierten tatsächlichen Beziehungen und Tabellen abbildet.
Das Metadata services Dies wird auch in Entity Client dargestellt und bietet eine zentralisierte API für den Zugriff auf Metadaten, die in Entity-, Mapping- und Speicherebenen gespeichert sind.
Objektdienst
Die Objektdienstschicht ist der Objektkontext, der die Interaktionssitzung zwischen den Anwendungen und der Datenquelle darstellt.
Der Objektkontext wird hauptsächlich verwendet, um verschiedene Vorgänge wie Hinzufügen, Löschen von Instanzen von Entitäten auszuführen und den geänderten Status mithilfe von Abfragen wieder in der Datenbank zu speichern.
Es ist die ORM-Schicht von Entity Framework, die das Datenergebnis für die Objektinstanzen von Entitäten darstellt.
Mit diesen Diensten können Entwickler einige der umfangreichen ORM-Funktionen wie Primärschlüsselzuordnung, Änderungsverfolgung usw. verwenden, indem sie Abfragen mit LINQ und Entity SQL schreiben.
Was ist neu in Entity Framework 6?
Framework verfügt über eine komplexe API, mit der Sie die Steuerung von der Modellierung bis zum Laufzeitverhalten detailliert steuern können. Ein Teil von Entity Framework 5 befindet sich in .NET. Ein weiterer Teil davon befindet sich in einer zusätzlichen Assembly, die mit NuGet verteilt wird.
Die Kernfunktionalität von Entity Framework ist in .NET Framework integriert.
Die Code First-Unterstützung ermöglicht es Entity Framework, Klassen anstelle eines visuellen Modells zu verwenden, und eine einfachere API für die Interaktion mit EF ist im NuGet-Paket enthalten.
Der Kern ist das, was die Abfrage, Änderungsverfolgung und die gesamte Umwandlung von Ihren Abfragen in SQL-Abfragen sowie von der Datenrückgabe in die Objekte ermöglicht.
Sie können das EF 5 NuGet-Paket sowohl mit .NET 4 als auch mit .NET 4.5 verwenden.
Ein großer Punkt der Verwirrung: .NET 4.5 hat den zentralen Entity Framework-APIs Unterstützung für Aufzählungen und räumliche Daten hinzugefügt. Wenn Sie also EF 5 mit .NET 4 verwenden, erhalten Sie diese neuen Funktionen nicht. Sie erhalten sie nur, wenn Sie EF5 mit .NET 4.5 kombinieren.
Lassen Sie uns nun einen Blick auf Entity Framework 6 werfen. Die Kern-APIs, die in .NET in Entity Framework 6 enthalten waren, sind jetzt Teil des NuGet-Pakets.
Es bedeutet -
Das gesamte Entity Framework befindet sich in dieser Assembly, die von NuGet vertrieben wird
Sie sind nicht auf .NET angewiesen, um bestimmte Funktionen wie die Entity Framework-Enum-Unterstützung und die Unterstützung spezieller Daten bereitzustellen.
Sie werden sehen, dass eine der Funktionen von EF6 darin besteht, dass es Aufzählungen und räumliche Daten für .NET 4 unterstützt
Um mit der Arbeit an Entity Framework zu beginnen, müssen Sie die folgenden Entwicklungstools installieren:
- Visual Studio 2013 oder höher
- SQL Server 2012 oder höher
- Entity Framework-Updates von NuGet Package
Microsoft bietet eine kostenlose Version von Visual Studio an, die auch SQL Server enthält und von www.visualstudio.com heruntergeladen werden kann .
Installation
Step 1- Führen Sie nach Abschluss des Downloads das Installationsprogramm aus. Der folgende Dialog wird angezeigt.
Step 2 - Klicken Sie auf die Schaltfläche Installieren und der Installationsvorgang wird gestartet.
Step 3- Nach erfolgreichem Abschluss des Installationsvorgangs wird der folgende Dialog angezeigt. Schließen Sie diesen Dialog und starten Sie Ihren Computer bei Bedarf neu.
Step 4- Öffnen Sie Visual Studio vom Startmenü aus, um den folgenden Dialog zu öffnen. Es wird eine Weile dauern, bis die Vorbereitung abgeschlossen ist.
Step 5 - Wenn alles erledigt ist, sehen Sie das Hauptfenster von Visual Studio.
Erstellen wir ein neues Projekt über Datei → Neu → Projekt
Step 1 - Wählen Sie Konsolenanwendung und klicken Sie auf OK.
Step 2 - Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf Ihr Projekt.
Step 3 - Wählen Sie NuGet-Pakete verwalten wie im obigen Bild gezeigt aus. Daraufhin wird das folgende Fenster in Visual Studio geöffnet.
Step 4 - Suchen Sie nach Entity Framework und installieren Sie die neueste Version, indem Sie auf die Schaltfläche Installieren klicken.
Step 5- Klicken Sie auf OK. Nach Abschluss der Installation wird in Ihrem Ausgabefenster die folgende Meldung angezeigt.
Sie können jetzt Ihre Bewerbung starten.
In diesem Tutorial verwenden wir eine einfache Universitätsdatenbank. Eine Universitätsdatenbank kann insgesamt viel komplexer sein, aber für Demo- und Lernzwecke verwenden wir die einfachste Form dieser Datenbank. Das folgende Diagramm enthält drei Tabellen.
- Student
- Course
- Enrollment
Wenn eine Begriffsdatenbank verwendet wird, fällt uns eines direkt ein, und das sind verschiedene Arten von Tabellen, die eine Beziehung haben. Es gibt drei Arten von Beziehungen zwischen Tabellen, und die Beziehung zwischen verschiedenen Tabellen hängt davon ab, wie die zugehörigen Spalten definiert sind.
- Eins-zu-viele-Beziehung
- Viele-zu-Viele-Beziehung
- Eins-zu-eins-Beziehung
Eins-zu-viele-Beziehung
Eins-zu-viele-Beziehung ist die häufigste Art von Beziehung. Bei 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. Im obigen Diagramm haben beispielsweise die Schüler- und die Einschreibungstabelle eine -zu-viele-Beziehung, jeder Schüler kann viele Einschreibungen haben, aber jede Einschreibung gehört nur einem Schüler.
Viele-zu-Viele-Beziehung
In einer Viele-zu-Viele-Beziehung kann eine Zeile in Tabelle A viele übereinstimmende Zeilen in Tabelle B haben und umgekehrt. Sie erstellen eine solche Beziehung, 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üler- und Kurstabelle eine Viele-zu-Viele-Beziehung, die durch definiert ist Eine Eins-zu-Viele-Beziehung von jeder dieser Tabellen zur Registrierungstabelle.
Eins-zu-eins-Beziehung
In einer Eins-zu-Eins-Beziehung kann eine Zeile in Tabelle A nicht mehr als eine übereinstimmende Zeile in Tabelle B haben und umgekehrt. Eine Eins-zu-Eins-Beziehung wird erstellt, wenn beide zugehörigen Spalten Primärschlüssel sind oder eindeutige Einschränkungen aufweisen.
Diese Art von Beziehung ist nicht üblich, da die meisten auf diese Weise zusammenhängenden Informationen eine All-in-One-Tabelle sind. Sie können eine Eins-zu-Eins-Beziehung zu - verwenden
- Teilen Sie eine Tabelle mit vielen Spalten.
- Isolieren Sie aus Sicherheitsgründen einen Teil einer Tabelle.
- Speichern Sie Daten, die nur von kurzer Dauer sind und durch einfaches Löschen der Tabelle leicht gelöscht werden können.
- Speichern Sie Informationen, die nur für eine Teilmenge der Haupttabelle gelten.
Das Entity Data Model (EDM) ist eine erweiterte Version des Entity-Relationship-Modells, das das konzeptionelle Modell der Daten unter Verwendung verschiedener Modellierungstechniken spezifiziert. Es bezieht sich auch auf eine Reihe von Konzepten, die die Datenstruktur unabhängig von ihrer gespeicherten Form beschreiben.
EDM unterstützt eine Reihe primitiver Datentypen, die Eigenschaften in einem konzeptionellen Modell definieren. Wir müssen drei Kernteile berücksichtigen, die die Grundlage für das Entity Framework bilden und zusammen als Entity Data Model bezeichnet werden. Im Folgenden sind die drei Kernteile von EDM aufgeführt.
- Das Speicherschemamodell
- Das konzeptionelle Modell
- Das Mapping-Modell
Das Speicherschemamodell
Das Speichermodell, das auch als SSDL (Storage Schema Definition Layer) bezeichnet wird, repräsentiert die schematische Darstellung des Backend-Datenspeichers.
Das konzeptionelle Modell
Das konzeptionelle Modell, auch als CSDL (Conceptual Schema Definition Layer) bezeichnet, ist das reale Entitätsmodell, gegen das wir unsere Abfragen schreiben.
Das Mapping-Modell
Die Zuordnungsebene ist nur eine Zuordnung zwischen dem Konzeptmodell und dem Speichermodell.
Das logische Schema und seine Zuordnung zum physischen Schema wird als EDM dargestellt.
Visual Studio bietet auch Entity Designer für die visuelle Erstellung des EDM und der Zuordnungsspezifikation.
Die Ausgabe des Tools ist die XML-Datei (* .edmx), die das Schema und die Zuordnung angibt.
Die Edmx-Datei enthält Entity Framework-Metadatenartefakte.
Schema-Definitionssprache
ADO.NET Entity Framework verwendet eine XML-basierte Datendefinitionssprache namens Schema Definition Language (SDL), um das EDM-Schema zu definieren.
Die SDL definiert die einfachen Typen, die anderen primitiven Typen ähnlich sind, einschließlich String, Int32, Double, Decimal und DateTime.
Eine Aufzählung, die eine Karte primitiver Werte und Namen definiert, wird ebenfalls als einfacher Typ betrachtet.
Aufzählungen werden nur ab Framework Version 5.0 unterstützt.
Komplexe Typen werden aus einer Aggregation anderer Typen erstellt. Eine Sammlung von Eigenschaften dieser Typen definiert einen Entitätstyp.
Das Datenmodell verfügt hauptsächlich über drei Schlüsselkonzepte zur Beschreibung der Datenstruktur:
- Entitätstyp
- Assoziationstyp
- Property
Entitätstyp
Der Entitätstyp ist der grundlegende Baustein zur Beschreibung der Datenstruktur in EDM.
In einem konzeptionellen Modell werden Entitätstypen aus Eigenschaften erstellt und beschreiben die Struktur von Konzepten der obersten Ebene, z. B. Studenten und Einschreibungen in einer Geschäftsanwendung.
Eine Entität repräsentiert ein bestimmtes Objekt, z. B. einen bestimmten Schüler oder eine bestimmte Einschreibung.
Jede Entität muss einen eindeutigen Entitätsschlüssel innerhalb eines Entitätssatzes haben. Ein Entitätssatz ist eine Sammlung von Instanzen eines bestimmten Entitätstyps. Entitätssätze (und Zuordnungssätze) werden logisch in einem Entitätscontainer gruppiert.
Vererbung wird mit Entitätstypen unterstützt, dh ein Entitätstyp kann von einem anderen abgeleitet werden.
Assoziationstyp
Es ist ein weiterer grundlegender Baustein zur Beschreibung von Beziehungen im EDM. In einem konzeptionellen Modell repräsentiert eine Zuordnung eine Beziehung zwischen zwei Entitätstypen wie Student und Einschreibung.
Jede Assoziation hat zwei Assoziationsenden, die die an der Assoziation beteiligten Entitätstypen angeben.
Jedes Assoziationsende gibt auch eine Assoziationsendmultiplizität an, die die Anzahl der Entitäten angibt, die sich an diesem Ende der Assoziation befinden können.
Eine Assoziationsendmultiplizität kann einen Wert von eins (1), null oder eins (0..1) oder viele (*) haben.
Auf Entitäten an einem Ende einer Zuordnung kann über Navigationseigenschaften oder über Fremdschlüssel zugegriffen werden, wenn sie für einen Entitätstyp verfügbar gemacht werden.
Eigentum
Entitätstypen enthalten Eigenschaften, die ihre Struktur und Eigenschaften definieren. Beispielsweise kann ein Student-Entitätstyp Eigenschaften wie Student-ID, Name usw. haben.
Eine Eigenschaft kann primitive Daten (z. B. eine Zeichenfolge, eine Ganzzahl oder einen Booleschen Wert) oder strukturierte Daten (z. B. einen komplexen Typ) enthalten.
Mit dem Entity Framework können Sie Daten mithilfe von CLR-Objekten (Common Language Runtime) abfragen, einfügen, aktualisieren und löschen, die als Entitäten bezeichnet werden. Das Entity Framework ordnet die in Ihrem Modell definierten Entitäten und Beziehungen einer Datenbank zu. Es bietet auch Einrichtungen für -
- Materialisieren Sie von der Datenbank zurückgegebene Daten als Entitätsobjekte
- Verfolgen Sie Änderungen, die an den Objekten vorgenommen wurden
- Parallelität behandeln
- Objektänderungen zurück in die Datenbank übertragen
- Binden Sie Objekte an Steuerelemente
Die primäre Klasse, die für die Interaktion mit Daten als Objekte verantwortlich ist, ist System.Data.Entity.DbContext. Die DbContext-API wird nicht als Teil von .NET Framework veröffentlicht. Um die Veröffentlichung neuer Funktionen für Code First und die DbContext-API flexibler und häufiger zu gestalten, verteilt das Entity Framework-Team EntityFramework.dll über die NuGet-Verteilungsfunktion von Microsoft.
Mit NuGet können Sie Verweise auf Ihre .NET-Projekte hinzufügen, indem Sie die relevanten DLLs direkt aus dem Web in Ihr Projekt ziehen.
Eine Visual Studio-Erweiterung namens Library Package Manager bietet eine einfache Möglichkeit, die entsprechende Assembly aus dem Web in Ihre Projekte zu ziehen.
Die DbContext-API zielt hauptsächlich darauf ab, die Interaktion mit Entity Framework zu vereinfachen.
Außerdem wird die Anzahl der Methoden und Eigenschaften reduziert, die Sie für den Zugriff auf häufig verwendete Aufgaben benötigen.
In früheren Versionen von Entity Framework waren diese Aufgaben häufig kompliziert zu erkennen und zu codieren.
Die Kontextklasse verwaltet die Entitätsobjekte zur Laufzeit. Dazu gehören das Auffüllen von Objekten mit Daten aus einer Datenbank, die Änderungsverfolgung und das Speichern von Daten in der Datenbank.
Definieren einer von DbContext abgeleiteten Klasse
Die empfohlene Methode zum Arbeiten mit dem Kontext besteht darin, eine Klasse zu definieren, die von DbContext abgeleitet ist und DbSet-Eigenschaften verfügbar macht, die Sammlungen der angegebenen Entitäten im Kontext darstellen. Wenn Sie mit dem EF Designer arbeiten, wird der Kontext für Sie generiert. Wenn Sie mit Code First arbeiten, schreiben Sie den Kontext normalerweise selbst.
Der folgende Code ist ein einfaches Beispiel, das zeigt, dass UniContext von DbContext abgeleitet ist.
Sie können mit DbSet automatische Eigenschaften wie Getter und Setter verwenden.
Es macht auch viel saubereren Code, aber Sie müssen ihn nicht zum Erstellen eines DbSet verwenden, wenn Sie keine andere Logik anwenden müssen.
public class UniContext : DbContext {
public UniContext() : base("UniContext") { }
public DbSet<Student> Students { get; set; }
public DbSet<Enrollment> Enrollments { get; set; }
public DbSet<Course> Courses { get; set; }
}
Bisher wurden mit EDM Kontextklassen generiert, die von der ObjectContext-Klasse abgeleitet wurden.
Die Arbeit mit ObjectContext war etwas komplex.
DbContext ist ein Wrapper um ObjectContext, der ObjectContext tatsächlich ähnelt und in allen Entwicklungsmodellen wie Code First, Model First und Database First nützlich und einfach ist.
Abfragen
Es gibt drei Arten von Abfragen, die Sie verwenden können, z.
- Hinzufügen einer neuen Entität.
- Ändern oder Aktualisieren der Eigenschaftswerte einer vorhandenen Entität.
- Löschen einer vorhandenen Entität.
Neue Entitäten hinzufügen
Das Hinzufügen eines neuen Objekts mit Entity Framework ist so einfach wie das Erstellen einer neuen Instanz Ihres Objekts und das Registrieren mit der Add-Methode in DbSet. Der folgende Code ist für den Fall vorgesehen, dass Sie der Datenbank einen neuen Schüler hinzufügen möchten.
private static void AddStudent() {
using (var context = new UniContext()) {
var student = new Student {
LastName = "Khan",
FirstMidName = "Ali",
EnrollmentDate = DateTime.Parse("2005-09-01")
};
context.Students.Add(student);
context.SaveChanges();
}
}
Bestehende Entitäten ändern
Das Ändern vorhandener Objekte ist so einfach wie das Aktualisieren des Werts, der den zu ändernden Eigenschaften zugewiesen ist, und das Aufrufen von SaveChanges. Im folgenden Code wurde der Nachname von Ali von Khan in Aslam geändert.
private static void AddStudent() {
private static void ChangeStudent() {
using (var context = new UniContext()) {
var student = (from d in context.Students
where d.FirstMidName == "Ali" select d).Single();
student.LastName = "Aslam";
context.SaveChanges();
}
}
}
Bestehende Entitäten löschen
Um eine Entität mit Entity Framework zu löschen, verwenden Sie die Remove-Methode in DbSet. Entfernen Sie Werke für vorhandene und neu hinzugefügte Entitäten. Wenn Sie Entfernen für eine Entität aufrufen, die hinzugefügt, aber noch nicht in der Datenbank gespeichert wurde, wird das Hinzufügen der Entität abgebrochen. Die Entität wird aus dem Änderungs-Tracker entfernt und vom DbContext nicht mehr verfolgt. Wenn Sie Entfernen für eine vorhandene Entität aufrufen, die geändert wird, wird die Entität beim nächsten Aufruf von SaveChanges zum Löschen registriert. Das folgende Beispiel zeigt eine Instanz, in der der Schüler aus der Datenbank entfernt wird, deren Vorname Ali ist.
private static void DeleteStudent() {
using (var context = new UniContext()) {
var bay = (from d in context.Students where d.FirstMidName == "Ali" select d).Single();
context.Students.Remove(bay);
context.SaveChanges();
}
}
In Entity Framework gibt es zwei Arten von Entitäten, mit denen Entwickler ihre eigenen benutzerdefinierten Datenklassen zusammen mit dem Datenmodell verwenden können, ohne Änderungen an den Datenklassen selbst vorzunehmen.
- POCO-Einheiten
- Dynamischer Proxy
POCO-Einheiten
POCO steht für "einfache" alte CLR-Objekte, die als vorhandene Domänenobjekte mit Ihrem Datenmodell verwendet werden können.
POCO-Datenklassen, die Entitäten zugeordnet sind, werden in einem Datenmodell definiert.
Es unterstützt auch die meisten der gleichen Abfrage-, Einfüge-, Aktualisierungs- und Löschverhalten wie Entitätstypen, die von den Entity Data Model-Tools generiert werden.
Mit der POCO-Vorlage können Sie aus einem konzeptionellen Modell persistenzunabhängige Entitätstypen generieren.
Schauen wir uns das folgende Beispiel des Conceptual Entity Data Model an.
So generieren Sie POCO-Entitäten für das oben genannte Entitätsmodell:
Step 1- Klicken Sie mit der rechten Maustaste auf das Designerfenster. Es wird der folgende Dialog angezeigt.
Step 2 - Wählen Sie das Element Codegenerierung hinzufügen ...
Step 3 - Wählen Sie den EF 6.x DbContext Generator aus, schreiben Sie den Namen und klicken Sie auf die Schaltfläche Hinzufügen.
In Ihrem Lösungs-Explorer sehen Sie, dass die Vorlagen POCODemo.Context.tt und POCODemo.tt generiert werden.
Der POCODemo.Context generiert den DbContext und die Objektgruppen, die Sie zurückgeben und zum Abfragen verwenden können, z. B. für den Kontext, Studenten und Kurse usw.
Die andere Vorlage behandelt alle Arten von Studenten, Kursen usw. Im Folgenden finden Sie den Code für die Studentenklasse, der automatisch aus dem Entitätsmodell generiert wird.
namespace ConsoleApplication1 {
using System;
using System.Collections.Generic;
public partial class Student {
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage",
"CA2214:DoNotCallOverridableMethodsInConstructors")]
public Student() {
this.Enrollments = new HashSet<Enrollment>();
}
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public System.DateTime EnrollmentDate { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage",
CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
}
Ähnliche Klassen werden für Kurs- und Einschreibungstabellen aus dem Entitätsmodell generiert.
Dynamischer Proxy
Beim Erstellen von Instanzen von POCO-Entitätstypen erstellt das Entity Framework häufig Instanzen eines dynamisch generierten abgeleiteten Typs, der als Proxy für die Entität fungiert. Man kann auch sagen, dass es sich um eine Laufzeit-Proxy-Klasse wie eine Wrapper-Klasse einer POCO-Entität handelt.
Sie können einige Eigenschaften der Entität überschreiben, um Aktionen automatisch auszuführen, wenn auf die Eigenschaft zugegriffen wird.
Dieser Mechanismus wird verwendet, um das verzögerte Laden von Beziehungen und die automatische Änderungsverfolgung zu unterstützen.
Diese Technik gilt auch für Modelle, die mit Code First und EF Designer erstellt wurden.
Wenn das Entity Framework das verzögerte Laden der zugehörigen Objekte unterstützen und Änderungen in POCO-Klassen verfolgen soll, müssen die POCO-Klassen die folgenden Anforderungen erfüllen:
Benutzerdefinierte Datenklassen müssen mit öffentlichem Zugriff deklariert werden.
Benutzerdefinierte Datenklassen dürfen nicht versiegelt werden.
Benutzerdefinierte Datenklassen dürfen nicht abstrakt sein.
Benutzerdefinierte Datenklassen müssen einen öffentlichen oder geschützten Konstruktor haben, der keine Parameter enthält.
Verwenden Sie einen geschützten Konstruktor ohne Parameter, wenn Sie mit der CreateObject-Methode einen Proxy für die POCO-Entität erstellen möchten.
Das Aufrufen der CreateObject-Methode garantiert nicht die Erstellung des Proxys: Die POCO-Klasse muss den anderen in diesem Thema beschriebenen Anforderungen entsprechen.
Die Klasse kann die Schnittstellen IEntityWithChangeTracker oder IEntityWithRelationships nicht implementieren, da die Proxy-Klassen diese Schnittstellen implementieren.
Die Option ProxyCreationEnabled muss auf true gesetzt sein.
Das folgende Beispiel zeigt eine dynamische Proxy-Entitätsklasse.
public partial class Course {
public Course() {
this.Enrollments = new HashSet<Enrollment>();
}
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
Um das Erstellen von Proxy-Objekten zu deaktivieren, setzen Sie den Wert der ProxyCreationEnabled-Eigenschaft auf false.
In relationalen Datenbanken besteht eine Beziehung zwischen relationalen Datenbanktabellen über Fremdschlüssel. Ein Fremdschlüssel (FK) ist eine Spalte oder eine Kombination von Spalten, mit der eine Verbindung zwischen den Daten in zwei Tabellen hergestellt und erzwungen wird. Das folgende Diagramm enthält drei Tabellen.
- Student
- Course
- Enrollment
Im obigen Diagramm sehen Sie eine Art Assoziation / Beziehung zwischen Tabellen. Es gibt drei Arten von Beziehungen zwischen Tabellen, und die Beziehung zwischen verschiedenen Tabellen hängt davon ab, wie die zugehörigen Spalten definiert sind.
- Eins-zu-viele-Beziehung
- Many-to-Many Relationship
- One-to-One Relationship
One-to-Many Relationship
A one-to-many relationship is the most common type of relationship.
In this type of relationship, a row in table A can have many matching rows in table B, but a row in table B can have only one matching row in table A.
The foreign key is defined in the table that represents the many end of the relationship.
For example, in the above diagram Student and Enrollment tables have one-tomany relationship, each student may have many enrollments, but each enrollment belongs to only one student.
In entity framework, these relationship can be created with code as well. Following is an example of Student and Enrollment classes which are associated with one to many relationship.
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 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; }
}
In the above code, you can see that Student class contains the collection of Enrollment, but Enrollment class has a single Student Object.
Many-to-Many Relationship
In many-to-many relationship, a row in table A can have many matching rows in table B, and vice versa.
You can create such a relationship by defining a third table, called a junction table, whose primary key consists of the foreign keys from both table A and table B.
For example, the Student and Course tables have many-to-many relationship that is defined by one-to-many relationship from each of these tables to the Enrollment table.
The following code contains the Course class and the above two classes, i.e., Student and Enrollment.
public class Course {
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
You can see that both Course class and Student class have collections of Enrollment objects which makes many-to-many relationship via junction class Enrollment.
One-to-One Relationship
In a one-to-one relationship, a row in table A can have no more than one matching row in table B, and vice versa.
A one-to-one relationship is created if both of the related columns are primary keys or have unique constraints.
In a one-to-one relationship, the primary key acts additionally as a foreign key and there is no separate foreign key column for either table.
This type of relationship is not common because most information related in this way would be all in one table. You might use a one-to-one relationship to −
- Divide a table with many columns.
- Isolate part of a table for security reasons.
- Store data that is short-lived and could be easily deleted by simply deleting the table.
- Store information that applies only to a subset of the main table.
The following code is to add another class name StudentProfile which contains the student email id and password.
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 virtual StudentProfile StudentProfile { get; set; }
}
public class StudentProfile {
public StudentProfile() {}
public int ID { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public virtual Student Student { get; set; }
}
You can see that Student entity class contains StudentProfile navigation property and StudentProfile contains Student navigation property.
Each student has only one Email and password to login in university domain. These information can be added to Student table but for security reasons it is separated to another table.
Lifetime
The lifetime of a context begins when the instance is created and ends when the instance is either disposed or garbage-collected.
Context lifetime is a very crucial decision to make when we use ORMs.
The context is performing like an entity cache, so it means it holds references to all the loaded entities which may grow very fast in memory consumption and it can also cause memory leaks.
In the below diagram, you can see the upper level of data workflow from application to database via Context and vice versa.
Entity Lifecycle
The Entity Lifecycle describes the process in which an Entity is created, added, modified, deleted, etc. Entities have many states during its lifetime. Before looking at how to retrieve entity state, let’s take a look at what is entity state. The state is an enum of type System.Data.EntityState that declares the following values −
Added: The entity is marked as added.
Deleted: The entity is marked as deleted.
Modified: The entity has been modified.
Unchanged: The entity hasn’t been modified.
Detached: The entity isn’t tracked.
State Changes in the Entity Lifecycle
Sometimes state of entities are set automatically by the context, but it can also be modified manually by the developer. Even though all the combinations of switches from one state to another are possible, but some of them are meaningless. For example, Added entity to the Deleted state, or vice versa.
Let’s discuss about different states.
Unchanged State
When an entity is Unchanged, it’s bound to the context but it hasn’t been modified.
By default, an entity retrieved from the database is in this state.
When an entity is attached to the context (with the Attach method), it similarly is in the Unchanged state.
The context can’t track changes to objects that it doesn’t reference, so when they’re attached it assumes they’re Unchanged.
Detached State
Detached is the default state of a newly created entity because the context can’t track the creation of any object in your code.
This is true even if you instantiate the entity inside a using block of the context.
Detached is even the state of entities retrieved from the database when tracking is disabled.
When an entity is detached, it isn’t bound to the context, so its state isn’t tracked.
It can be disposed of, modified, used in combination with other classes, or used in any other way you might need.
Because there is no context tracking it, it has no meaning to Entity Framework.
Added State
When an entity is in the Added state, you have few options. In fact, you can only detach it from the context.
Naturally, even if you modify some property, the state remains Added, because moving it to Modified, Unchanged, or Deleted makes no sense.
It’s a new entity and has no correspondence with a row in the database.
This is a fundamental prerequisite for being in one of those states (but this rule isn’t enforced by the context).
Modified State
When an entity is modified, that means it was in Unchanged state and then some property was changed.
After an entity enters the Modified state, it can move to the Detached or Deleted state, but it can’t roll back to the Unchanged state even if you manually restore the original values.
It can’t even be changed to Added, unless you detach and add the entity to the context, because a row with this ID already exists in the database, and you would get a runtime exception when persisting it.
Deleted State
An entity enters the Deleted state because it was Unchanged or Modified and then the DeleteObject method was used.
This is the most restrictive state, because it’s pointless changing from this state to any other value but Detached.
The using statement if you want all the resources that the context controls to be disposed at the end of the block. When you use the using statement, then compiler automatically creates a try/finally block and calls dispose in the finally block.
using (var context = new UniContext()) {
var student = new Student {
LastName = "Khan",
FirstMidName = "Ali",
EnrollmentDate = DateTime.Parse("2005-09-01")
};
context.Students.Add(student);
context.SaveChanges();
}
When working with long-running context consider the following −
As you load more objects and their references into memory, the memory consumption of the context may increase rapidly. This may cause performance issues.
Remember to dispose of the context when it is no longer required.
If an exception causes the context to be in an unrecoverable state, the whole application may terminate.
The chances of running into concurrency-related issues increases as the gap between the time when the data is queried and updated grows.
When working with Web applications, use a context instance per request.
When working with Windows Presentation Foundation (WPF) or Windows Forms, use a context instance per form. This lets you use change-tracking functionality that context provides.
Rules of Thumb
Web Applications
It is now a common and best practice that for web applications, context is used per request.
In web applications, we deal with requests that are very short but holds all the server transaction they are therefore the proper duration for the context to live in.
Desktop Applications
For desktop application, like Win Forms/WPF, etc. the context is used per form/dialog/page.
Since we don’t want to have the context as a singleton for our application we will dispose it when we move from one form to another.
In this way, we will gain a lot of the context’s abilities and won’t suffer from the implications of long running contexts.
The Entity Framework provides three approaches to create an entity model and each one has their own pros and cons.
- Code First
- Database First
- Model First
In this chapter, we will briefly describe the code first approach. Some developers prefer to work with the Designer in Code while others would rather just work with their code. For those developers, Entity Framework has a modeling workflow referred to as Code First.
Code First modeling workflow targets a database that doesn’t exist and Code First will create it.
It can also be used if you have an empty database and then Code First will add new tables too.
Code First allows you to define your model using C# or VB.Net classes.
Additional configuration can optionally be performed using attributes on your classes and properties or by using a fluent API.
Why Code First?
Code First is really made up of a set of puzzle pieces. First are your domain classes.
The domain classes have nothing to do with Entity Framework. They're just the items of your business domain.
Entity Framework, then, has a context that manages the interaction between those classes and your database.
The context is not specific to Code First. It's an Entity Framework feature.
Code First adds a model builder that inspects your classes that the context is managing, and then uses a set of rules or conventions to determine how those classes and the relationships describe a model, and how that model should map to your database.
All of this happens at runtime. You'll never see this model, it's just in memory.
Code First has the ability to use that model to create a database if required.
It can also update the database if the model changes, using a feature called Code First Migrations.
In this chapter, let us learn how to create an entity data model in the designer using the workflow referred to as Model First.
Model First is great for when you're starting a new project where the database doesn't even exist yet.
The model is stored in an EDMX file and can be viewed and edited in the Entity Framework Designer.
In Model First, you define your model in an Entity Framework designer then generate SQL, which will create database schema to match your model and then you execute the SQL to create the schema in your database.
The classes that you interact with in your application are automatically generated from the EDMX file.
Following is a simple example of creating a new console project using Model First approach.
Step 1 − Open Visual Studio and select File → New → Project
Step 2 − Select Installed → Templates → Visual C# → Windows from left pane and then in middle pane, select Console Application.
Step 3 − Enter EFModelFirstDemo in the Name field.
Step 4 − To create model, first right-click on your console project in solution explorer and select Add → New Items…
The following dialog will open.
Step 5 − Select ADO.NET Entity Data Model from middle pane and enter name ModelFirstDemoDB in the Name field.
Step 6 − Click Add button which will launch the Entity Data Model Wizard dialog.
Step 7 − Select Empty EF Designer model and click Next button. The Entity Framework Designer opens with a blank model. Now we can start adding entities, properties and associations to the model.
Step 8 − Right-click on the design surface and select Properties. In the Properties window, change the Entity Container Name to ModelFirstDemoDBContext.
Step 9 − Right-click on the design surface and select Add New → Entity…
Add Entity dialog will open as shown in the following image.
Step 10 − Enter Student as entity name and Student Id as property name and click Ok.
Step 11 − Right-click on the new entity on the design surface and select Add New → Scalar Property, enter Name as the name of the property.
Step 12 − Enter FirstName and then add another two scalar properties such as LastName and EnrollmentDate.
Step 13 − Add two more Entities Course and Enrollment by following all the steps mentioned above and also add some Scalar properties as shown in the following steps.
Step 14 − We have three entities in Visual Designer, let’s add some association or relationship between them.
Step 15 − Right-click on the design surface and select Add New → Association…
Step 16 − Make one end of the relationship point to Student with a multiplicity of one and the other end point to Enrollment with a multiplicity of many.
Step 17 − This means that a Student has many Enrollments and Enrollment belongs to one Student.
Step 18 − Ensure the Add foreign key properties to 'Post' Entity box is checked and click OK.
Step 19 − Similarly, add one more association between Course and Enrollment.
Step 20 − Your data model will look like the following screen after adding associations between entities.
We now have a simple model that we can generate a database from and use to read and write data. Let's go ahead and generate the database.
Step 1 − Right-click on the design surface and select Generate Database from Model…
Step 2 − You can select existing database or create a new connection by clicking on New Connection…
Step 3 − To create new Database, click on New Connection…
Step 4 − Enter Server name and database name.
Step 5 − Click Next.
Step 6 − Click Finish. This will add *.edmx.sql file in the project. You can execute DDL scripts in Visual Studio by opening .sql file, then right-click and select Execute.
Step 7 − The following dialog will be displayed to connect to database.
Step 8 − On successful execution, you will see the following message.
Step 9 - Gehen Sie zum Server-Explorer. Sie werden sehen, dass die Datenbank mit drei angegebenen Tabellen erstellt wird.
Als nächstes müssen wir unser Modell austauschen, um Code zu generieren, der die DbContext-API verwendet.
Step 1 - Klicken Sie im EF Designer mit der rechten Maustaste auf eine leere Stelle Ihres Modells und wählen Sie Codegenerierungselement hinzufügen…
Sie werden sehen, dass das folgende Dialogfeld "Neues Element hinzufügen" geöffnet wird.
Step 2 - Wählen Sie im mittleren Bereich EF 6.x DbContext Generator aus und geben Sie ModelFirstDemoModel in das Feld Name ein.
Step 3 - In Ihrem Lösungs-Explorer sehen Sie, dass die Vorlagen ModelFirstDemoModel.Context.tt und ModelFirstDemoModel.tt generiert werden.
Der ModelFirstDemoModel.Context generiert den DbCcontext und die Objektgruppen, die Sie zurückgeben und zum Abfragen verwenden können, z. B. für den Kontext, Studenten und Kurse usw.
Die andere Vorlage behandelt alle Arten von Studenten, Kursen usw. Es folgt die Studentenklasse, die automatisch aus dem Entitätsmodell generiert wird.
Es folgt der C # -Code, in dem einige Daten eingegeben und aus der Datenbank abgerufen werden.
using System;
using System.Linq;
namespace EFModelFirstDemo {
class Program {
static void Main(string[] args) {
using (var db = new ModelFirstDemoDBContext()) {
// Create and save a new Student
Console.Write("Enter a name for a new Student: ");
var firstName = Console.ReadLine();
var student = new Student {
StudentID = 1,
FirstName = firstName
};
db.Students.Add(student);
db.SaveChanges();
var query = from b in db.Students
orderby b.FirstName select b;
Console.WriteLine("All student in the database:");
foreach (var item in query) {
Console.WriteLine(item.FirstName);
}
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
}
}
Wenn der obige Code ausgeführt wird, erhalten Sie die folgende Ausgabe:
Enter a name for a new Student:
Ali Khan
All student in the database:
Ali Khan
Press any key to exit...
Wir empfehlen Ihnen, das obige Beispiel zum besseren Verständnis Schritt für Schritt auszuführen.
In diesem Kapitel erfahren Sie, wie Sie ein Entitätsdatenmodell mit dem Database First-Ansatz erstellen.
Der Database First-Ansatz bietet eine Alternative zu den Code First- und Model First-Ansätzen für das Entity Data-Modell. Es erstellt Modellcodes (Klassen, Eigenschaften, DbContext usw.) aus der Datenbank im Projekt, und diese Klassen werden zur Verbindung zwischen Datenbank und Controller.
Der Database First Approach erstellt das Entity Framework aus einer vorhandenen Datenbank. Wir verwenden alle anderen Funktionen, wie die Modell- / Datenbanksynchronisierung und die Codegenerierung, genauso wie wir sie im Model First-Ansatz verwendet haben.
Nehmen wir ein einfaches Beispiel. Wir haben bereits eine Datenbank, die 3 Tabellen enthält, wie im folgenden Bild gezeigt.
Step 1 - Erstellen wir ein neues Konsolenprojekt mit dem Namen DatabaseFirstDemo.
Step 2 - Um das Modell zu erstellen, klicken Sie zunächst im Solution Explorer mit der rechten Maustaste auf Ihr Konsolenprojekt und wählen Sie Hinzufügen → Neue Elemente…
Step 3 - Wählen Sie im mittleren Bereich ADO.NET-Entitätsdatenmodell aus und geben Sie den Namen DatabaseFirstModel in das Feld Name ein.
Step 4 - Klicken Sie auf die Schaltfläche Hinzufügen, um das Dialogfeld Assistent für Entitätsdatenmodelle zu öffnen.
Step 5 - Wählen Sie EF Designer aus der Datenbank aus und klicken Sie auf Weiter.
Step 6 - Wählen Sie die vorhandene Datenbank aus und klicken Sie auf Weiter.
Step 7 - Wählen Sie Entity Framework 6.x und klicken Sie auf Weiter.
Step 8 - Wählen Sie alle Tabellenansichten und gespeicherten Prozeduren aus, die Sie einschließen möchten, und klicken Sie auf Fertig stellen.
Sie werden sehen, dass Entitätsmodell- und POCO-Klassen aus der Datenbank generiert werden.
Lassen Sie uns nun alle Schüler aus der Datenbank abrufen, indem wir den folgenden Code in die Datei program.cs schreiben.
using System;
using System.Linq;
namespace DatabaseFirstDemo {
class Program {
static void Main(string[] args) {
using (var db = new UniContextEntities()) {
var query = from b in db.Students
orderby b.FirstMidName select b;
Console.WriteLine("All All student in the database:");
foreach (var item in query) {
Console.WriteLine(item.FirstMidName +" "+ item.LastName);
}
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
}
}
Wenn das obige Programm ausgeführt wird, erhalten Sie die folgende Ausgabe:
All student in the database:
Ali Khan
Arturo finand
Bill Gates
Carson Alexander
Gytis Barzdukas
Laura Norman
Meredith Alonso
Nino Olivetto
Peggy Justice
Yan Li
Press any key to exit...
Wenn das obige Programm ausgeführt wird, sehen Sie alle Namen der Schüler, die zuvor in die Datenbank eingegeben wurden.
Wir empfehlen Ihnen, das obige Beispiel zum besseren Verständnis Schritt für Schritt auszuführen.
In diesem Kapitel konzentrieren wir uns darauf, Modelle mit dem Designer oder der Datenbank zuerst zu erstellen oder nur Code zuerst zu verwenden. Im Folgenden finden Sie einige Richtlinien, anhand derer Sie entscheiden können, welchen Modellierungsworkflow Sie auswählen möchten.
Wir haben bereits Beispiele für Code First-Modellierung, Database First-Modellierung und einen Model First-Modellierungsworkflow gesehen.
Die Workflows "Database First" und "Model First" verwendeten den Designer, aber einer beginnt mit der Datenbank, um ein Modell zu erstellen, und der andere beginnt mit dem Modell, um eine Datenbank zu erstellen.
Für Entwickler, die Visual Designer plus Codegenerierung nicht verwenden möchten, bietet Entity Framework einen völlig anderen Workflow namens Code First.
Der typische Workflow für Code First eignet sich hervorragend für brandneue Anwendungen, in denen Sie nicht einmal über eine Datenbank verfügen. Sie definieren Ihre Klassen und Ihren Code und lassen Code First herausfinden, wie Ihre Datenbank aussehen soll.
Es ist auch möglich, Code First mit einer Datenbank zu starten, was Code First zu einem Widerspruch macht. Es gibt jedoch ein Tool, mit dem Sie eine Datenbank in Klassen zurückentwickeln können. Dies ist eine hervorragende Möglichkeit, einen Vorsprung bei der Codierung zu erlangen.
Schauen wir uns angesichts dieser Optionen den Entscheidungsbaum an.
Wenn Sie lieber mit einem Visual Designer in generiertem Code arbeiten möchten, sollten Sie einen der Workflows auswählen, an denen EF Designer beteiligt ist. Wenn Ihre Datenbank bereits vorhanden ist, ist Database First Ihr Pfad.
Wenn Sie einen Visual Designer für ein brandneues Projekt ohne Datenbank verwenden möchten, sollten Sie Model First verwenden.
Wenn Sie nur mit Code und nicht mit einem Designer arbeiten möchten, ist Code First wahrscheinlich genau das Richtige für Sie, zusammen mit der Option, das Tool zu verwenden, mit dem die Datenbank in Klassen zurückentwickelt wird.
Wenn Sie bereits Klassen haben, ist es am besten, diese mit Code First zu verwenden.
In den vorherigen Kapiteln haben Sie drei verschiedene Methoden zum Definieren eines Entitätsdatenmodells kennengelernt.
Zwei davon, Database First und Model First, hingen vom Entity Framework-Designer in Kombination mit der Codegenerierung ab.
Mit dem dritten Code First können Sie einen visuellen Designer überspringen und einfach Ihren eigenen Code schreiben.
Unabhängig davon, welchen Pfad Sie wählen, erhalten Sie Domänenklassen. Mit einer oder mehreren Entity Framework DbContext-Klassen können Sie Daten abrufen und beibehalten, die für diese Klassen relevant sind.
Die DbContext-API in Ihren Anwendungen wird als Brücke zwischen Ihren Klassen und Ihrer Datenbank verwendet. Der DbContext ist eine der wichtigsten Klassen im Entity Framework.
Es ermöglicht das Ausdrücken und Ausführen von Abfragen.
Es nimmt Abfrageergebnisse aus der Datenbank und wandelt sie in Instanzen unserer Modellklassen um.
Es kann Änderungen an Entitäten verfolgen, einschließlich Hinzufügen und Löschen, und dann die Erstellung von Anweisungen zum Einfügen, Aktualisieren und Löschen auslösen, die bei Bedarf an die Datenbank gesendet werden.
Im Folgenden finden Sie die Kontextklassen für Domain-Anzeigen, für die in diesem Kapitel verschiedene Vorgänge ausgeführt werden. Dies ist das gleiche Beispiel, das wir im Chapater Database First Approach erstellt haben.
Implementierung der Kontextklasse
using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.Core.Objects;
using System.Linq;
namespace DatabaseFirstDemo {
public partial class UniContextEntities : DbContext {
public UniContextEntities(): base("name = UniContextEntities") {}
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<Course> Courses { get; set; }
public virtual DbSet<Enrollment> Enrollments { get; set; }
public virtual DbSet<Student> Students { get; set; }
}
}
Implementierung von Domänenklassen
Kursklasse
namespace DatabaseFirstDemo {
using System;
using System.Collections.Generic;
public partial class Course {
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage",
"CA2214:DoNotCallOverridableMethodsInConstructors")]
public Course() {
this.Enrollments = new HashSet<Enrollment>();
}
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage",
"CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
}
Schülerklasse
namespace DatabaseFirstDemo {
using System;
using System.Collections.Generic;
public partial class Student {
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage",
"CA2214:DoNotCallOverridableMethodsInConstructors")]
public Student() {
this.Enrollments = new HashSet<Enrollment>();
}
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public System.DateTime EnrollmentDate { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage",
"CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
}
Einschreibungsklasse
namespace DatabaseFirstDemo {
using System;
using System.Collections.Generic;
public partial class Enrollment {
public int EnrollmentID { get; set; }
public int CourseID { get; set; }
public int StudentID { get; set; }
public Nullable<int> Grade { get; set; }
public virtual Course Course { get; set; }
public virtual Student Student { get; set; }
}
}
Operation erstellen
Das Hinzufügen eines neuen Objekts mit Entity Framework ist so einfach wie das Erstellen einer neuen Instanz Ihres Objekts und das Registrieren mit der Add-Methode in DbSet. Mit dem folgenden Code können Sie der Datenbank einen neuen Schüler hinzufügen.
class Program {
static void Main(string[] args) {
var newStudent = new Student();
//set student name
newStudent.FirstMidName = "Bill";
newStudent.LastName = "Gates";
newStudent.EnrollmentDate = DateTime.Parse("2015-10-21");
newStudent.ID = 100;
//create DBContext object
using (var dbCtx = new UniContextEntities()) {
//Add Student object into Students DBset
dbCtx.Students.Add(newStudent);
// call SaveChanges method to save student into database
dbCtx.SaveChanges();
}
}
}
Vorgang aktualisieren
Das Ändern vorhandener Objekte ist so einfach wie das Aktualisieren des Werts, der den zu ändernden Eigenschaften zugewiesen ist, und das Aufrufen von SaveChanges. Der folgende Code wird beispielsweise verwendet, um den Nachnamen von Ali von Khan in Aslam zu ändern.
using (var context = new UniContextEntities()) {
var student = (from d in context.Students where d.FirstMidName == "Ali" select d).Single();
student.LastName = "Aslam";
context.SaveChanges();
}
Vorgang löschen
Um eine Entität mit Entity Framework zu löschen, verwenden Sie die Remove-Methode in DbSet. Entfernen Sie Werke für vorhandene und neu hinzugefügte Entitäten. Wenn Sie Entfernen für eine Entität aufrufen, die hinzugefügt, aber noch nicht in der Datenbank gespeichert wurde, wird das Hinzufügen der Entität abgebrochen. Die Entität wird aus dem Änderungs-Tracker entfernt und vom DbContext nicht mehr verfolgt. Wenn Sie Entfernen für eine vorhandene Entität aufrufen, die geändert wird, wird die Entität beim nächsten Aufruf von SaveChanges zum Löschen registriert. Das folgende Beispiel zeigt einen Code, bei dem der Schüler aus der Datenbank entfernt wird, deren Vorname Ali ist.
using (var context = new UniContextEntities()) {
var bay = (from d in context.Students where d.FirstMidName == "Ali" select d).Single();
context.Students.Remove(bay);
context.SaveChanges();
}
Betrieb lesen
Das Lesen der vorhandenen Daten aus der Datenbank ist sehr einfach. Im Folgenden finden Sie den Code, in dem alle Daten aus der Schülertabelle abgerufen werden. Anschließend wird ein Programm mit dem Vor- und Nachnamen der Schüler in alphabetischer Reihenfolge angezeigt.
using (var db = new UniContextEntities()) {
var query = from b in db.Students orderby b.FirstMidName select b;
Console.WriteLine("All All student in the database:");
foreach (var item in query) {
Console.WriteLine(item.FirstMidName +" "+ item.LastName);
}
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
Jeder Datenzugriffsentwickler hat Schwierigkeiten bei der Beantwortung der Frage zur Datengleichzeitigkeit: "Was passiert, wenn mehr als eine Person dieselben Daten gleichzeitig bearbeitet?"
Die Glücklicheren unter uns beschäftigen sich mit Geschäftsregeln, die besagen: "Kein Problem, der letzte gewinnt."
In diesem Fall ist die Parallelität kein Problem. Wahrscheinlicher ist es nicht so einfach, und es gibt keine Silberkugel, um jedes Szenario auf einmal zu lösen.
Standardmäßig verwendet das Entity Framework den Pfad "Letzter Gewinn". Dies bedeutet, dass das neueste Update auch dann angewendet wird, wenn jemand anderes die Daten zwischen dem Abrufen der Daten und dem Speichern der Zeitdaten aktualisiert hat.
Nehmen wir ein Beispiel, um es besser zu verstehen. Im folgenden Beispiel wird eine neue Spalte VersionNo in der Kurstabelle hinzugefügt.
Gehen Sie zum Designer, klicken Sie mit der rechten Maustaste auf das Designerfenster und wählen Sie Modell aus Datenbank aktualisieren aus.
Sie werden sehen, dass eine weitere Spalte in der Kursentität hinzugefügt wird.
Klicken Sie mit der rechten Maustaste auf die neu erstellte Spalte VersionNo, wählen Sie Eigenschaften aus und ändern Sie den ConcurrencyMode in Fest, wie in der folgenden Abbildung dargestellt.
Wenn der ConcurrencyMode von Course.VersionNo auf Fixed gesetzt ist, sucht der Befehl Update bei jeder Aktualisierung eines Kurses mit seinem EntityKey und seiner VersionNo-Eigenschaft nach dem Kurs.
Schauen wir uns ein einfaches Szenario an. Zwei Benutzer rufen denselben Kurs gleichzeitig ab, und Benutzer 1 ändert den Titel dieses Kurses in Mathematik und speichert Änderungen vor Benutzer 2. Später, wenn Benutzer 2 den Titel dieses Kurses ändert, der abgerufen wurde, bevor Benutzer 1 seine Änderungen darin speichert Fall Benutzer 2 erhält eine Parallelitätsausnahme"User2: Optimistic Concurrency exception occured".
using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
namespace DatabaseFirstDemo {
class Program {
static void Main(string[] args) {
Course c1 = null;
Course c2 = null;
//User 1 gets Course
using (var context = new UniContextEntities()) {
context.Configuration.ProxyCreationEnabled = false;
c1 = context.Courses.Where(s ⇒ s.CourseID == 1).Single();
}
//User 2 also get the same Course
using (var context = new UniContextEntities()) {
context.Configuration.ProxyCreationEnabled = false;
c2 = context.Courses.Where(s ⇒ s.CourseID == 1).Single();
}
//User 1 updates Course Title
c1.Title = "Edited from user1";
//User 2 updates Course Title
c2.Title = "Edited from user2";
//User 1 saves changes first
using (var context = new UniContextEntities()) {
try {
context.Entry(c1).State = EntityState.Modified;
context.SaveChanges();
} catch (DbUpdateConcurrencyException ex) {
Console.WriteLine("User1: Optimistic Concurrency exception occurred");
}
}
//User 2 saves changes after User 1.
//User 2 will get concurrency exection
//because CreateOrModifiedDate is different in the database
using (var context = new UniContextEntities()) {
try {
context.Entry(c2).State = EntityState.Modified;
context.SaveChanges();
} catch (DbUpdateConcurrencyException ex) {
Console.WriteLine("User2: Optimistic Concurrency exception occurred");
}
}
}
}
}
In allen Versionen von Entity Framework, wann immer Sie ausführen SaveChanges()Um die Datenbank einzufügen, zu aktualisieren oder zu löschen, wird diese Operation vom Framework in eine Transaktion eingeschlossen. Wenn Sie SaveChanges aufrufen, startet der Kontext automatisch eine Transaktion und schreibt sie fest oder setzt sie zurück, je nachdem, ob die Persistenz erfolgreich war.
Dies ist alles für Sie transparent und Sie müssen sich nie damit befassen.
Diese Transaktion dauert nur lange genug, um den Vorgang auszuführen, und wird dann abgeschlossen.
Wenn Sie eine andere solche Operation ausführen, wird eine neue Transaktion gestartet.
Entity Framework 6 bietet Folgendes:
Database.BeginTransaction ()
Es ist eine einfache und einfachere Methode innerhalb eines vorhandenen DbContext, Transaktionen für Benutzer zu starten und abzuschließen.
Es ermöglicht die Kombination mehrerer Vorgänge innerhalb derselben Transaktion, sodass entweder alle festgeschrieben oder alle als eine zurückgesetzt werden.
Außerdem kann der Benutzer die Isolationsstufe für die Transaktion einfacher festlegen.
Database.UseTransaction ()
Damit kann der DbContext eine Transaktion verwenden, die außerhalb des Entity Framework gestartet wurde.
Schauen wir uns das folgende Beispiel an, in dem mehrere Vorgänge in einer einzigen Transaktion ausgeführt werden. Der Code lautet wie folgt:
class Program {
static void Main(string[] args) {
using (var context = new UniContextEntities()) {
using (var dbContextTransaction = context.Database.BeginTransaction()) {
try {
Student student = new Student() {
ID = 200,
FirstMidName = "Ali",
LastName = "Khan",
EnrollmentDate = DateTime.Parse("2015-12-1")
};
context.Students.Add(student);
context.Database.ExecuteSqlCommand(@"UPDATE Course SET Title =
'Calculus'" + "WHERE CourseID = 1045");
var query = context.Courses.Where(c ⇒ c.CourseID == 1045);
foreach (var item in query) {
Console.WriteLine(item.CourseID.ToString()
+ " " + item.Title + " " + item.Credits);
}
context.SaveChanges();
var query1 = context.Students.Where(s ⇒ s.ID == 200);
foreach (var item in query1) {
Console.WriteLine(item.ID.ToString()
+ " " + item.FirstMidName + " " + item.LastName);
}
dbContextTransaction.Commit();
} catch (Exception) {
dbContextTransaction.Rollback();
}
}
}
}
}
Um eine Transaktion zu starten, muss die zugrunde liegende Geschäftsverbindung geöffnet sein.
Wenn Sie also Database.BeginTransaction () aufrufen, wird die Verbindung geöffnet, sofern sie noch nicht geöffnet ist.
Wenn DbContextTransaction die Verbindung geöffnet hat, wird sie geschlossen, wenn Dispose () aufgerufen wird.
Eine Ansicht ist ein Objekt, das Daten enthält, die durch eine vordefinierte Abfrage erhalten wurden. Eine Ansicht ist ein virtuelles Objekt oder eine virtuelle Tabelle, deren Ergebnismenge aus einer Abfrage abgeleitet wird. Es ist einer realen Tabelle sehr ähnlich, da es Spalten und Zeilen von Daten enthält. Im Folgenden sind einige typische Verwendungszwecke von Ansichten aufgeführt:
- Filtern Sie Daten der zugrunde liegenden Tabellen
- Filtern Sie Daten aus Sicherheitsgründen
- Zentralisieren Sie Daten, die auf mehrere Server verteilt sind
- Erstellen Sie einen wiederverwendbaren Datensatz
Ansichten können auf ähnliche Weise wie Tabellen verwendet werden. Um view als Entität zu verwenden, müssen Sie zuerst Datenbankansichten zu EDM hinzufügen. Nachdem Sie Ihrem Modell Ansichten hinzugefügt haben, können Sie damit wie mit normalen Entitäten arbeiten, mit Ausnahme der Vorgänge Erstellen, Aktualisieren und Löschen.
Lassen Sie uns einen Blick darauf werfen, wie Sie dem Modell Ansichten aus der Datenbank hinzufügen.
Step 1 - Erstellen Sie ein neues Konsolenanwendungsprojekt.
Step 2 - Klicken Sie im Solution Explorer mit der rechten Maustaste auf das Projekt und wählen Sie Hinzufügen → Neues Element.
Step 3 - Wählen Sie im mittleren Bereich ADO.NET-Entitätsdatenmodell aus und geben Sie den Namen ViewModel in das Feld Name ein.
Step 4 - Klicken Sie auf die Schaltfläche Hinzufügen, um das Dialogfeld Assistent für Entitätsdatenmodelle zu öffnen.
Step 5 - Wählen Sie EF Designer aus der Datenbank aus und klicken Sie auf Weiter.
Step 6 - Wählen Sie die vorhandene Datenbank aus und klicken Sie auf Weiter.
Step 7 - Wählen Sie Entity Framework 6.x und klicken Sie auf Weiter.
Step 8 - Wählen Sie Tabellen und Ansichten aus Ihrer Datenbank aus und klicken Sie auf Fertig stellen.
Sie können im Designerfenster sehen, dass eine Ansicht erstellt wurde, und Sie können sie im Programm als Entität verwenden.
Im Lösungs-Explorer können Sie sehen, dass die MyView-Klasse auch aus der Datenbank generiert wird.
Nehmen wir ein Beispiel, in dem alle Daten aus der Ansicht abgerufen werden. Es folgt der Code -
class Program {
static void Main(string[] args) {
using (var db = new UniContextEntities()) {
var query = from b in db.MyViews
orderby b.FirstMidName select b;
Console.WriteLine("All student in the database:");
foreach (var item in query) {
Console.WriteLine(item.FirstMidName + " " + item.LastName);
}
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
}
Wenn der obige Code ausgeführt wird, erhalten Sie die folgende Ausgabe:
All student in the database:
Ali Khan
Arturo finand
Bill Gates
Carson Alexander
Gytis Barzdukas
Laura Norman
Meredith Alonso
Nino Olivetto
Peggy Justice
Yan Li
Press any key to exit...
Wir empfehlen Ihnen, das obige Beispiel zum besseren Verständnis Schritt für Schritt auszuführen.
Ein Index ist eine Datenstruktur auf der Festplatte, die auf Tabellen und Ansichten basiert. Indizes machen das Abrufen von Daten in den meisten Fällen schneller und effizienter. Das Überladen einer Tabelle oder Ansicht mit Indizes kann jedoch die Leistung anderer Vorgänge wie Einfügungen oder Aktualisierungen unangenehm beeinträchtigen.
Die Indizierung ist die neue Funktion im Entity Framework, mit der Sie die Leistung Ihrer Code First-Anwendung verbessern können, indem Sie die Zeit reduzieren, die zum Abfragen von Daten aus der Datenbank erforderlich ist.
Sie können Ihrer Datenbank mithilfe von Indizes hinzufügen Index Attribut und überschreiben Sie die Standardeinstellung Unique und Clustered Einstellungen, um den Index zu erhalten, der am besten zu Ihrem Szenario passt.
Schauen wir uns den folgenden Code an, in dem das Indexattribut in der Kursklasse für CourseID hinzugefügt wird.
public partial class Course {
public Course() {
this.Enrollments = new HashSet<Enrollment>();
}
[Index]
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
public byte[] VersionNo { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
Der oben erstellte Schlüssel ist nicht eindeutig und nicht gruppiert. Es sind Überladungen verfügbar, um diese Standardeinstellungen zu überschreiben.
Um einen Index zu einem Clustered-Index zu machen, müssen Sie IsClustered = true angeben
In ähnlicher Weise können Sie einen Index auch zu einem eindeutigen Index machen, indem Sie IsUnique = true angeben
Schauen wir uns den folgenden C # -Code an, in dem ein Index gruppiert und eindeutig ist.
public partial class Course {
public Course() {
this.Enrollments = new HashSet<Enrollment>();
}
[Index(IsClustered = true, IsUnique = true)]
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
public byte[] VersionNo { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
Das Indexattribut kann verwendet werden, um einen eindeutigen Index in der Datenbank zu erstellen. Dies bedeutet jedoch nicht, dass EF beim Umgang mit Beziehungen usw. über die Eindeutigkeit der Spalte nachdenken kann. Diese Funktion wird normalerweise als Unterstützung für „eindeutige Einschränkungen“ bezeichnet.
Mit dem Entity Framework können Sie gespeicherte Prozeduren im Entity Data Model anstelle oder in Kombination mit der automatischen Befehlsgenerierung verwenden.
Sie können gespeicherte Prozeduren verwenden, um vordefinierte Logik für Datenbanktabellen auszuführen, und viele Organisationen verfügen über Richtlinien, die die Verwendung dieser gespeicherten Prozeduren erfordern.
Es kann auch festgelegt werden, dass EF Ihre gespeicherten Prozeduren zum Einfügen, Aktualisieren oder Löschen von Entitäten verwenden soll.
Obwohl die dynamisch erstellten Befehle sicher, effizient und im Allgemeinen so gut oder besser sind als die, die Sie möglicherweise selbst schreiben, gibt es viele Fälle, in denen bereits gespeicherte Prozeduren vorhanden sind und Ihre Unternehmenspraktiken die direkte Verwendung der Tabellen einschränken können.
Alternativ möchten Sie möglicherweise nur die explizite Kontrolle darüber haben, was im Geschäft ausgeführt wird, und möchten lieber gespeicherte Prozeduren erstellen.
Im folgenden Beispiel wird ein neues Projekt über Datei → Neu → Projekt erstellt.
Step 1 - Wählen Sie im mittleren Bereich die Konsolenanwendung aus und geben Sie StoredProceduresDemo in das Namensfeld ein.
Step 2 - Klicken Sie im Server Explorer mit der rechten Maustaste auf Ihre Datenbank.
Step 3 - Wählen Sie Neue Abfrage und geben Sie den folgenden Code in den T-SQL-Editor ein, um eine neue Tabelle in Ihre Datenbank aufzunehmen.
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id =
OBJECT_ID(N'[dbo].[StudentGrade]') AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[StudentGrade](
[EnrollmentID] [int] IDENTITY(1,1) NOT NULL,
[CourseID] [int] NOT NULL,
[StudentID] [int] NOT NULL,
[Grade] [decimal](3, 2) NULL,
CONSTRAINT [PK_StudentGrade] PRIMARY KEY CLUSTERED (
[EnrollmentID] ASC
)
WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
END
GO
Step 4 - Klicken Sie mit der rechten Maustaste auf den Editor und wählen Sie Ausführen.
Step 5- Klicken Sie mit der rechten Maustaste auf Ihre Datenbank und klicken Sie auf Aktualisieren. Sie sehen die neu hinzugefügte Tabelle in Ihrer Datenbank.
Step 6 - Klicken Sie im Server-Explorer erneut mit der rechten Maustaste auf Ihre Datenbank.
Step 7 - Wählen Sie Neue Abfrage und geben Sie den folgenden Code in den T-SQL-Editor ein, um eine gespeicherte Prozedur in Ihrer Datenbank hinzuzufügen, die die Noten der Schüler zurückgibt.
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id =
OBJECT_ID(N'[dbo].[GetStudentGrades]') AND type in (N'P', N'PC'))
BEGIN
EXEC dbo.sp_executesql @statement = N'
CREATE PROCEDURE [dbo].[GetStudentGrades]
@StudentID int
AS
SELECT EnrollmentID, Grade, CourseID, StudentID FROM dbo.StudentGrade
WHERE StudentID = @StudentID
'
END
GO
Step 8 - Klicken Sie mit der rechten Maustaste auf den Editor und wählen Sie Ausführen.
Step 9- Klicken Sie mit der rechten Maustaste auf Ihre Datenbank und klicken Sie auf Aktualisieren. Sie werden sehen, dass eine gespeicherte Prozedur in Ihrer Datenbank erstellt wird.
Step 10 - Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf den Projektnamen und wählen Sie Hinzufügen → Neues Element.
Step 11 - Wählen Sie dann im Bereich Vorlagen die Option ADO.NET-Entitätsdatenmodell aus.
Step 12 - Geben Sie SPModel als Namen ein und klicken Sie dann auf Hinzufügen.
Step 13 - Wählen Sie im Dialogfeld Modellinhalt auswählen die Option EF-Designer aus der Datenbank aus und klicken Sie dann auf Weiter.
Step 14 - Wählen Sie Ihre Datenbank aus und klicken Sie auf Weiter.
Step 15 - Klicken Sie im Dialogfeld Datenbankobjekte auswählen auf Tabellen, Ansichten.
Step 16 - Wählen Sie die Funktion GetStudentGradesForCourse unter dem Knoten Gespeicherte Prozeduren und Funktionen aus und klicken Sie auf Fertig stellen.
Step 17 - Wählen Sie Ansicht → Andere Fenster → Browser für Entitätsdatenmodelle und klicken Sie mit der rechten Maustaste unter Funktionsimporte auf GetStudentGrades und wählen Sie Bearbeiten.
Es wird der folgende Dialog erzeugt.
Step 18 - Klicken Sie auf das Optionsfeld Entitäten, wählen Sie StudentGrade aus der Combobox als Rückgabetyp dieser gespeicherten Prozedur aus und klicken Sie auf OK.
Schauen wir uns den folgenden C # -Code an, in dem alle Noten abgerufen werden, indem die Schüler-ID als Parameter in der gespeicherten Prozedur GetStudentGrades übergeben wird.
class Program {
static void Main(string[] args) {
using (var context = new UniContextEntities()) {
int studentID = 22;
var studentGrades = context.GetStudentGrades(studentID);
foreach (var student in studentGrades) {
Console.WriteLine("Course ID: {0}, Title: {1}, Grade: {2} ",
student.CourseID, student.Course.Title, student.Grade);
}
Console.ReadKey();
}
}
}
Wenn der obige Code kompiliert und ausgeführt wird, erhalten Sie die folgende Ausgabe:
Course ID: 4022, Title: Microeconomics, Grade: 3.00
Course ID: 4041, Title: Macroeconomics, Grade: 3.50
Wir empfehlen, dass Sie das obige Beispiel zum besseren Verständnis Schritt für Schritt ausführen.
In diesem Kapitel sehen wir uns an, wie Sie Änderungen an Entitäten vornehmen, die nicht von einem Kontext verfolgt werden. Entitäten, die nicht von einem Kontext verfolgt werden, werden als "getrennte" Entitäten bezeichnet.
Bei den meisten einstufigen Anwendungen, bei denen die Benutzeroberfläche und die Datenbankzugriffsebenen im selben Anwendungsprozess ausgeführt werden, führen Sie wahrscheinlich nur Vorgänge für Entitäten aus, die von einem Kontext verfolgt werden.
Vorgänge mit nicht verbundenen Entitäten sind in N-Tier-Anwendungen weitaus häufiger.
Bei N-Tier-Anwendungen werden einige Daten auf einem Server abgerufen und über das Netzwerk an einen Clientcomputer zurückgegeben.
Die Clientanwendung bearbeitet diese Daten dann, bevor sie an den Server zurückgegeben werden, damit sie beibehalten werden.
Im Folgenden sind die beiden Schritte aufgeführt, die mit dem Diagramm für nicht verbundene Entitäten oder sogar für eine einzelne nicht verbundene Entität ausgeführt werden müssen.
Hängen Sie Entitäten an die neue Kontextinstanz an und machen Sie den Kontext auf diese Entitäten aufmerksam.
Stellen Sie die entsprechenden EntityStates manuell auf diese Entitäten ein.
Schauen wir uns den folgenden Code an, in dem die Schülerentität mit zwei Registrierungsentitäten hinzugefügt wird.
class Program {
static void Main(string[] args) {
var student = new Student {
ID = 1001,
FirstMidName = "Wasim",
LastName = "Akram",
EnrollmentDate = DateTime.Parse("2015-10-10"),
Enrollments = new List<Enrollment> {
new Enrollment{EnrollmentID = 2001,CourseID = 4022, StudentID = 1001 },
new Enrollment{EnrollmentID = 2002,CourseID = 4025, StudentID = 1001 },
}
};
using (var context = new UniContextEntities()) {
context.Students.Add(student);
Console.WriteLine("New Student ({0} {1}): {2}",
student.FirstMidName, student.LastName, context.Entry(student).State);
foreach (var enrollment in student.Enrollments) {
Console.WriteLine("Enrollment ID: {0} State: {1}",
enrollment.EnrollmentID, context.Entry(enrollment).State);
}
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
}
Der Code erstellt eine neue Student-Instanz, die in ihrer Enrollments-Eigenschaft auch auf zwei neue Enrollment-Instanzen verweist.
Anschließend wird der neue Schüler mithilfe der Add-Methode einem Kontext hinzugefügt.
Sobald der Student hinzugefügt wurde, verwendet der Code die DbContext.Entry-Methode, um Zugriff auf die Änderungsverfolgungsinformationen zu erhalten, die Entity Framework über den neuen Student hat.
Aus diesen Änderungsverfolgungsinformationen wird die State-Eigenschaft verwendet, um den aktuellen Status der Entität zu schreiben.
Dieser Vorgang wird dann für jede der neu erstellten Anmeldungen wiederholt, auf die vom neuen Schüler verwiesen wird. Wenn Sie die Anwendung ausführen, erhalten Sie die folgende Ausgabe:
New Student (Wasim Akram): Added
Enrollment ID: 2001 State: Added
Enrollment ID: 2002 State: Added
Press any key to exit...
Während DbSet.Add verwendet wird, um Entity Framework über neue Entitäten zu informieren, wird DbSet.Attach verwendet, um Entity Framework über vorhandene Entitäten zu informieren. Die Attach-Methode markiert eine Entität im unveränderten Zustand.
Schauen wir uns den folgenden C # -Code an, in dem eine nicht verbundene Entität mit DbContext verbunden ist.
class Program {
static void Main(string[] args) {
var student = new Student {
ID = 1001,
FirstMidName = "Wasim",
LastName = "Akram",
EnrollmentDate = DateTime.Parse("2015-10-10"),
Enrollments = new List<Enrollment> {
new Enrollment { EnrollmentID = 2001, CourseID = 4022, StudentID = 1001 },
new Enrollment { EnrollmentID = 2002, CourseID = 4025, StudentID = 1001 },
}
};
using (var context = new UniContextEntities()) {
context.Students.Attach(student);
Console.WriteLine("New Student ({0} {1}): {2}",
student.FirstMidName, student.LastName, context.Entry(student).State);
foreach (var enrollment in student.Enrollments) {
Console.WriteLine("Enrollment ID: {0} State: {1}", enrollment.EnrollmentID,
context.Entry(enrollment).State);
}
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
}
Wenn der obige Code mit der Attach () -Methode ausgeführt wird, erhalten Sie die folgende Ausgabe.
New Student (Wasim Akram): Unchanged
Enrollment ID: 2001 State: Unchanged
Enrollment ID: 2002 State: Unchanged
Press any key to exit...
In diesem Kapitel erfahren Sie, wie Sie Tabellenwertfunktionen (TVFs) mit dem Entity Framework Designer zuordnen und eine TVF aus einer LINQ-Abfrage aufrufen.
TVFs werden derzeit nur im Database First-Workflow unterstützt.
Es wurde erstmals in Entity Framework Version 5 eingeführt.
Um die TVFs verwenden zu können, müssen Sie auf .NET Framework 4.5 oder höher abzielen.
Es ist gespeicherten Prozeduren sehr ähnlich, aber mit einem wesentlichen Unterschied, dh das Ergebnis einer TVF ist zusammensetzbar. Dies bedeutet, dass die Ergebnisse einer TVF in einer LINQ-Abfrage verwendet werden können, während die Ergebnisse einer gespeicherten Prozedur dies nicht können.
Schauen wir uns das folgende Beispiel zum Erstellen eines neuen Projekts über Datei → Neu → Projekt an.
Step 1 - Wählen Sie im mittleren Bereich die Konsolenanwendung aus und geben Sie TableValuedFunctionDemo in das Namensfeld ein.
Step 2 - Klicken Sie im Server Explorer mit der rechten Maustaste auf Ihre Datenbank.
Step 3 - Wählen Sie Neue Abfrage und geben Sie den folgenden Code in den T-SQL-Editor ein, um eine neue Tabelle in Ihre Datenbank aufzunehmen.
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id =
OBJECT_ID(N'[dbo].[StudentGrade]') AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[StudentGrade](
[EnrollmentID] [int] IDENTITY(1,1) NOT NULL,
[CourseID] [int] NOT NULL,
[StudentID] [int] NOT NULL,
[Grade] [decimal](3, 2) NULL,
CONSTRAINT [PK_StudentGrade] PRIMARY KEY CLUSTERED ([EnrollmentID] ASC)
WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
END
GO
Step 4 - Klicken Sie mit der rechten Maustaste auf den Editor und wählen Sie Ausführen.
Step 5- Klicken Sie mit der rechten Maustaste auf Ihre Datenbank und klicken Sie auf Aktualisieren. Sie sehen die neu hinzugefügte Tabelle in Ihrer Datenbank.
Step 6- Erstellen Sie nun eine Funktion, die die Noten der Schüler für den Kurs zurückgibt. Geben Sie den folgenden Code in den T-SQL-Editor ein.
CREATE FUNCTION [dbo].[GetStudentGradesForCourse]
(@CourseID INT)
RETURNS TABLE
RETURN
SELECT [EnrollmentID],
[CourseID],
[StudentID],
[Grade]
FROM [dbo].[StudentGrade]
WHERE CourseID = @CourseID
Step 7 - Klicken Sie mit der rechten Maustaste auf den Editor und wählen Sie Ausführen.
Jetzt können Sie sehen, dass die Funktion erstellt wurde.
Step 8 - Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf den Projektnamen und wählen Sie Hinzufügen → Neues Element.
Step 9 - Wählen Sie dann im Bereich Vorlagen die Option ADO.NET-Entitätsdatenmodell aus.
Step 10 - Geben Sie TVFModel als Namen ein und klicken Sie dann auf Hinzufügen.
Step 11 - Wählen Sie im Dialogfeld Modellinhalt auswählen die Option EF-Designer aus der Datenbank aus und klicken Sie dann auf Weiter.
Step 12 - Wählen Sie Ihre Datenbank aus und klicken Sie auf Weiter.
Step 13 - Wählen Sie im Dialogfeld Datenbankobjekte auswählen Tabellen und Ansichten aus.
Step 14 - Wählen Sie die Funktion GetStudentGradesForCourse unter dem Knoten Gespeicherte Prozeduren und Funktionen aus und klicken Sie auf Fertig stellen.
Step 15 - Wählen Sie Ansicht → Andere Fenster → Browser für Entitätsdatenmodelle, klicken Sie mit der rechten Maustaste auf GetStudentGradesForCourse unter Funktionsimporte und wählen Sie Bearbeiten.
Sie sehen den folgenden Dialog.
Step 16 - Klicken Sie auf das Optionsfeld Entitäten, wählen Sie in der Combobox die Option Registrierung als Rückgabetyp für diese Funktion aus und klicken Sie auf OK.
Werfen wir einen Blick auf den folgenden C # -Code, in dem alle Noten der Schüler abgerufen werden, die in der Datenbank mit der Kurs-ID = 4022 eingeschrieben sind.
class Program {
static void Main(string[] args) {
using (var context = new UniContextEntities()) {
var CourseID = 4022;
// Return all the best students in the Microeconomics class.
var students = context.GetStudentGradesForCourse(CourseID);
foreach (var result in students) {
Console.WriteLine("Student ID: {0}, Grade: {1}",
result.StudentID, result.Grade);
}
Console.ReadKey();
}
}
}
Wenn der obige Code kompiliert und ausgeführt wird, erhalten Sie die folgende Ausgabe:
Student ID: 1, Grade: 2
Student ID: 4, Grade: 4
Student ID: 9, Grade: 3.5
Wir empfehlen, dass Sie das obige Beispiel zum besseren Verständnis Schritt für Schritt ausführen.
In Entity Framework können Sie mit Ihren Entitätsklassen mithilfe von LINQ Abfragen durchführen. Sie können Abfragen mit Raw SQL auch direkt mit DbCOntext für die Datenbank ausführen. Die Techniken können gleichermaßen auf Modelle angewendet werden, die mit Code First und EF Designer erstellt wurden.
SQL-Abfrage für vorhandene Entität
Mit der SqlQuery-Methode in DbSet kann eine unformatierte SQL-Abfrage geschrieben werden, die Entitätsinstanzen zurückgibt. Die zurückgegebenen Objekte werden vom Kontext genauso verfolgt, wie sie wären, wenn sie von einer LINQ-Abfrage zurückgegeben würden. Zum Beispiel -
class Program {
static void Main(string[] args) {
using (var context = new UniContextEntities()) {
var students = context.Students.SqlQuery("SELECT * FROM dbo.Student").ToList();
foreach (var student in students) {
string name = student.FirstMidName + " " + student.LastName;
Console.WriteLine("ID: {0}, Name: {1}, \tEnrollment Date {2} ",
student.ID, name, student.EnrollmentDate.ToString());
}
Console.ReadKey();
}
}
}
The above code will retrieve all the students from the database.
SQL Query for Non-entity Types
A SQL query returning instances of any type, including primitive types, can be created using the SqlQuery method on the Database class. For example −
class Program {
static void Main(string[] args) {
using (var context = new UniContextEntities()) {
var studentNames = context.Database.SqlQuery
<string>("SELECT FirstMidName FROM dbo.Student").ToList();
foreach (var student in studentNames) {
Console.WriteLine("Name: {0}", student);
}
Console.ReadKey();
}
}
}
SQL Commands to the Database
ExecuteSqlCommnad method is used in sending non-query commands to the database, such as the Insert, Update or Delete command. Let’s take a look at the following code in which student’s first name is updated as ID = 1
class Program {
static void Main(string[] args) {
using (var context = new UniContextEntities()) {
//Update command
int noOfRowUpdated = context.Database.ExecuteSqlCommand("Update
student set FirstMidName = 'Ali' where ID = 1");
context.SaveChanges();
var student = context.Students.SqlQuery("SELECT * FROM
dbo.Student where ID = 1").Single();
string name = student.FirstMidName + " " + student.LastName;
Console.WriteLine("ID: {0}, Name: {1}, \tEnrollment Date {2} ",
student.ID, name, student.EnrollmentDate.ToString());
Console.ReadKey();
}
}
}
The above code will retrieve all the students’ first name from the database.
In Entity Framework, this feature will allow you to define a property on a domain class that is an enum type and map it to a database column of an integer type. Entity Framework will then convert the database value to and from the relevant enum as it queries and saves data.
Enumerated types have all sorts of benefits when working with properties that have a fixed number of responses.
The security and reliability of an application both increase when you use enumerations.
Enumeration makes it much harder for the user to make mistakes, and issues such as injection attacks are nonexistent.
In Entity Framework, an enumeration can have the following underlying types −
- Byte
- Int16
- Int32
- Int64
- SByte
The default underlying type of the enumeration elements is int.
By default, the first enumerator has the value 0, and the value of each successive enumerator is increased by 1.
Schauen wir uns das folgende Beispiel an, in dem wir eine Entität in Designer erstellen und dann einige Eigenschaften hinzufügen.
Step 1 - Erstellen Sie ein neues Projekt über die Menüoption Datei → Neu → Projekt.
Step 2 - Wählen Sie im linken Bereich die Konsolenanwendung aus.
Step 3 - Geben Sie EFEnumDemo als Namen des Projekts ein und klicken Sie auf OK.
Step 4 - Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf den Projektnamen und wählen Sie die Menüoption Hinzufügen → Neues Element.
Step 5 - Wählen Sie im Bereich Vorlagen die Option ADO.NET-Entitätsdatenmodell aus.
Step 6 - Geben Sie EFEnumModel.edmx als Dateinamen ein und klicken Sie dann auf Hinzufügen.
Step 7 - Wählen Sie auf der Seite Assistent für Entitätsdatenmodelle die Option Leeres EF-Designermodell aus.
Step 8 - Klicken Sie auf Fertig stellen
Step 9 - Klicken Sie dann mit der rechten Maustaste auf das Designerfenster und wählen Sie Hinzufügen → Entität.
Das Dialogfeld Neue Entität wird wie in der folgenden Abbildung dargestellt angezeigt.
Step 10 - Geben Sie Abteilung als Entitätsname und DeptID als Eigenschaftsname ein, belassen Sie den Eigenschaftstyp als Int32 und klicken Sie auf OK.
Step 11 - Klicken Sie mit der rechten Maustaste auf die Entität und wählen Sie Neu hinzufügen → Skalareigenschaft.
Step 12 - Benennen Sie die neue Eigenschaft in DeptName um.
Step 13 - Ändern Sie den Typ der neuen Eigenschaft in Int32 (standardmäßig ist die neue Eigenschaft vom Typ String).
Step 14 - Um den Typ zu ändern, öffnen Sie das Eigenschaftenfenster und ändern Sie die Type-Eigenschaft in Int32.
Step 15 - Klicken Sie im Entity Framework Designer mit der rechten Maustaste auf die Eigenschaft Name und wählen Sie In Aufzählung konvertieren.
Step 16 - Geben Sie im Dialogfeld Aufzählungstyp hinzufügen Abteilungsnamen für den Aufzählungstypnamen ein, ändern Sie den zugrunde liegenden Typ in Int32 und fügen Sie dem Typ die folgenden Elemente hinzu: Physik, Chemie, Computer und Wirtschaft.
Step 17 - Klicken Sie auf OK.
Wenn Sie zum Modellbrowser-Fenster wechseln, sehen Sie, dass der Typ auch dem Knoten Aufzählungstypen hinzugefügt wurde.
Lassen Sie uns eine Datenbank aus dem Modell generieren, indem Sie alle im Kapitel Modell erster Ansatz genannten Schritte ausführen.
Step 1 - Klicken Sie mit der rechten Maustaste auf die Oberfläche von Entity Designer und wählen Sie Datenbank aus Modell generieren.
Das Dialogfeld Wählen Sie Ihre Datenverbindung des Assistenten zum Generieren von Datenbanken wird angezeigt.
Step 2 - Klicken Sie auf die Schaltfläche Neue Verbindung.
Step 3 - Geben Sie den Servernamen und EnumDemo für die Datenbank ein und klicken Sie auf OK.
Step 4 - Ein Dialogfeld mit der Frage, ob Sie eine neue Datenbank erstellen möchten, wird angezeigt. Klicken Sie auf Ja.
Step 5- Klicken Sie auf Weiter. Der Assistent zum Erstellen einer Datenbank generiert eine DDL (Data Definition Language) zum Erstellen einer Datenbank. Klicken Sie nun auf Fertig stellen.
Step 6 - Klicken Sie mit der rechten Maustaste auf T-SQL Editor und wählen Sie Ausführen.
Step 7 - Um das generierte Schema anzuzeigen, klicken Sie im SQL Server-Objekt-Explorer mit der rechten Maustaste auf den Datenbanknamen und wählen Sie Aktualisieren.
Sie sehen die Tabelle Abteilungen in der Datenbank.
Schauen wir uns das folgende Beispiel an, in dem einige neue Abteilungsobjekte zum Kontext hinzugefügt und gespeichert werden. Und dann die Computerabteilung abrufen.
class Program {
static void Main(string[] args) {
using (var context = new EFEnumModelContainer()) {
context.Departments.Add(new Department { DeptName = DepartmentNames.Physics});
context.Departments.Add(new Department { DeptName = DepartmentNames.Computer});
context.Departments.Add(new Department { DeptName = DepartmentNames.Chemistry});
context.Departments.Add(new Department { DeptName = DepartmentNames.Economics});
context.SaveChanges();
var department = (
from d in context.Departments
where d.DeptName == DepartmentNames.Computer
select d
).FirstOrDefault();
Console.WriteLine(
"Department ID: {0}, Department Name: {1}",
department.DeptID, department.DeptName
);
Console.ReadKey();
}
}
}
Wenn der obige Code ausgeführt wird, erhalten Sie die folgende Ausgabe:
Department ID: 2, Department Name: Computer
Wir empfehlen, dass Sie das obige Beispiel zum besseren Verständnis Schritt für Schritt ausführen.
Asynchronous programmingbeinhaltet das Ausführen von Operationen im Hintergrund, damit der Hauptthread seine eigenen Operationen fortsetzen kann. Auf diese Weise kann der Hauptthread die Benutzeroberfläche reaktionsfähig halten, während der Hintergrundthread die vorliegende Aufgabe verarbeitet.
Entity Framework 6.0 unterstützt asynchrone Vorgänge zum Abfragen und Speichern von Daten.
Asynchrone Vorgänge können Ihrer Anwendung auf folgende Weise helfen:
- Machen Sie Ihre Anwendung reaktionsschneller für Benutzerinteraktionen
- Verbessern Sie die Gesamtleistung Ihrer Anwendung
Sie können asynchrone Vorgänge auf verschiedene Arten ausführen. In .NET Framework 4.5 wurden jedoch asynchrone / warten-Schlüsselwörter eingeführt, die Ihre Arbeit vereinfachen.
Das einzige, was Sie befolgen müssen, ist das asynchrone / wartende Muster, wie im folgenden Codefragment dargestellt.
Schauen wir uns das folgende Beispiel an (ohne async / await zu verwenden), in dem die DatabaseOperations-Methode einen neuen Schüler in der Datenbank speichert und dann alle Schüler aus der Datenbank abruft. Am Ende wird eine zusätzliche Nachricht auf der Konsole gedruckt.
class Program {
static void Main(string[] args) {
Console.WriteLine("Database Operations Started");
DatabaseOperations();
Console.WriteLine();
Console.WriteLine("Database Operations Completed");
Console.WriteLine();
Console.WriteLine("Entity Framework Tutorials");
Console.ReadKey();
}
public static void DatabaseOperations() {
using (var context = new UniContextEntities()) {
// Create a new student and save it
context.Students.Add(new Student {
FirstMidName = "Akram",
LastName = "Khan",
EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())});
Console.WriteLine("Calling SaveChanges.");
context.SaveChanges();
Console.WriteLine("SaveChanges completed.");
// Query for all Students ordered by first name
var students = (from s in context.Students
orderby s.FirstMidName select s).ToList();
// Write all students out to Console
Console.WriteLine();
Console.WriteLine("All Student:");
foreach (var student in students) {
string name = student.FirstMidName + " " + student.LastName;
Console.WriteLine(" " + name);
}
}
}
}
Wenn der obige Code ausgeführt wird, erhalten Sie die folgende Ausgabe:
Calling SaveChanges.
SaveChanges completed.
All Student:
Akram Khan
Ali Khan
Ali Alexander
Arturo Anand
Bill Gates
Gytis Barzdukas
Laura Nornan
Meredith fllonso
Nino Olioetto
Peggy Justice
Yan Li
Entity Framework Tutorials
Verwenden wir die neue asynchrone Funktion, warten auf Schlüsselwörter und nehmen die folgenden Änderungen an Program.cs vor
Fügen Sie den System.Data.Entity-Namespace hinzu, der asynchrone EF-Erweiterungsmethoden bereitstellt.
Fügen Sie den Namespace System.Threading.Tasks hinzu, mit dem wir den Aufgabentyp verwenden können.
Aktualisieren DatabaseOperations markiert werden als async und zurück a Task.
Rufen Sie die Async-Version von SaveChanges auf und warten Sie auf deren Abschluss.
Rufen Sie die Async-Version von ToList auf und warten Sie auf das Ergebnis.
class Program {
static void Main(string[] args) {
var task = DatabaseOperations();
Console.WriteLine();
Console.WriteLine("Entity Framework Tutorials");
task.Wait();
Console.ReadKey();
}
public static async Task DatabaseOperations() {
using (var context = new UniContextEntities()) {
// Create a new blog and save it
context.Students.Add(new Student {
FirstMidName = "Salman",
LastName = "Khan",
EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())});
Console.WriteLine("Calling SaveChanges.");
await context.SaveChangesAsync();
Console.WriteLine("SaveChanges completed.");
// Query for all Students ordered by first name
var students = await (from s in context.Students
orderby s.FirstMidName select s).ToListAsync();
// Write all students out to Console
Console.WriteLine();
Console.WriteLine("All Student:");
foreach (var student in students) {
string name = student.FirstMidName + " " + student.LastName;
Console.WriteLine(" " + name);
}
}
}
}
Bei der Ausführung wird die folgende Ausgabe erzeugt.
Calling SaveChanges.
Entity Framework Tutorials
SaveChanges completed.
All Student:
Akram Khan
Ali Khan
Ali Alexander
Arturo Anand
Bill Gates
Gytis Barzdukas
Laura Nornan
Meredith fllonso
Nino Olioetto
Peggy Justice
Salman Khan
Yan Li
Jetzt, da der Code asynchron ist, können Sie einen anderen Ausführungsfluss Ihres Programms beobachten.
SaveChanges beginnt, den neuen Student in die Datenbank zu verschieben, und dann kehrt die DatabaseOperations-Methode zurück (obwohl die Ausführung noch nicht abgeschlossen ist), und der Programmablauf in der Main-Methode wird fortgesetzt.
Die Nachricht wird dann in die Konsole geschrieben.
Der verwaltete Thread wird beim Wait-Aufruf blockiert, bis der Datenbankvorgang abgeschlossen ist. Sobald dies abgeschlossen ist, wird der Rest unserer DatabaseOperations ausgeführt.
SaveChanges ist abgeschlossen.
Der gesamte Schüler wurde aus der Datenbank abgerufen und in die Konsole geschrieben.
Wir empfehlen, dass Sie das obige Beispiel zum besseren Verständnis Schritt für Schritt ausführen.
Mit Entity Framework können Sie jetzt vom Entity Framework profitieren, ohne dass jeder Teil Ihrer Anwendung das Entity Framework kennen muss, wodurch Entitäten von der Infrastruktur getrennt werden. Sie können Klassen erstellen, die sich auf ihre Geschäftsregeln konzentrieren können, unabhängig davon, wie sie beibehalten werden (wo die Daten gespeichert werden und wie die Daten zwischen Ihren Objekten hin und her gelangen).
Persistente ignorante Entitäten erstellen
Im vorhergehenden Absatz wurde eine Methode beschrieben, die die Quelle der von ihr verwendeten Daten nicht genau kennt. Dies unterstreicht die Essenz der Persistenz-Ignoranz, wenn es Ihren Klassen und vielen unserer Anwendungsebenen um sie herum egal ist, wie die Daten gespeichert werden.
Wenn Sie in der .NET 3.5-Version von Entity Framework bereits vorhandene Klassen verwenden möchten, müssen Sie diese ändern, indem Sie sie zwingen, von EntityObject abzuleiten.
In .NET 4 ist dies nicht mehr erforderlich. Sie müssen Ihre Entitäten nicht ändern, damit sie an Entity Framework-Vorgängen teilnehmen können.
Auf diese Weise können wir Anwendungen erstellen, die eine lose Kopplung und Trennung von Bedenken umfassen.
Mit diesen Codierungsmustern befassen sich Ihre Klassen nur mit ihren eigenen Jobs, und viele Ebenen Ihrer Anwendung, einschließlich der Benutzeroberfläche, sind nicht von externer Logik abhängig, z. B. die Entity Framework-APIs. Diese externen APIs können jedoch mit unseren interagieren Entitäten.
Es gibt zwei Möglichkeiten (verbunden und getrennt), wenn eine Entität mit dem Entity Framework beibehalten wird. Beide Wege haben ihre eigene Bedeutung. Im Fall eines verbundenen Szenarios werden die Änderungen vom Kontext verfolgt. Im Fall eines nicht verbundenen Szenarios müssen wir den Kontext über den Status der Entität informieren.
Verbundene Szenarien
Verbundenes Szenario ist, wenn eine Entität aus der Datenbank abgerufen und im selben Kontext geändert wird. Nehmen wir für ein verbundenes Szenario an, wir haben einen Windows-Dienst und führen einige Geschäftsvorgänge mit dieser Entität aus, sodass wir den Kontext öffnen, alle Entitäten durchlaufen, unsere Geschäftsvorgänge ausführen und die Änderungen dann im selben Kontext wie wir speichern am Anfang geöffnet.
Schauen wir uns das folgende Beispiel an, in dem Schüler aus der Datenbank abgerufen werden, den Vornamen der Schüler aktualisieren und dann die Änderungen in der Datenbank speichern.
class Program {
static void Main(string[] args) {
using (var context = new MyContext()) {
var studentList = context.Students.ToList();
foreach (var stdnt in studentList) {
stdnt.FirstMidName = "Edited " + stdnt.FirstMidName;
}
context.SaveChanges();
//// Display all Students from the database
var students = (from s in context.Students
orderby s.FirstMidName select s).ToList<Student>();
Console.WriteLine("Retrieve all Students from the database:");
foreach (var stdnt in students) {
string name = stdnt.FirstMidName + " " + stdnt.LastName;
Console.WriteLine("ID: {0}, Name: {1}", stdnt.ID, name);
}
Console.ReadKey();
}
}
}
Wenn der obige Code kompiliert und ausgeführt wird, erhalten Sie die folgende Ausgabe und sehen, dass das bearbeitete Wort vor dem Vornamen angehängt ist, wie in der folgenden Ausgabe gezeigt.
Retrieve all Students from the database:
ID: 1, Name: Edited Edited Alain Bomer
ID: 2, Name: Edited Edited Mark Upston
Getrennte Szenarien
Ein nicht verbundenes Szenario ist, wenn eine Entität aus der Datenbank abgerufen und in einem anderen Kontext geändert wird. Angenommen, wir möchten einige Daten in einer Präsentationsschicht anzeigen und verwenden eine n-Tier-Anwendung. Daher ist es besser, den Kontext zu öffnen, die Daten abzurufen und schließlich den Kontext zu schließen. Da wir hier die Daten abgerufen und den Kontext geschlossen haben, werden die von uns abgerufenen Entitäten nicht mehr verfolgt, und dies ist das nicht verbundene Szenario.
Schauen wir uns den folgenden Code an, in dem eine neue getrennte Student-Entität mithilfe der Add-Methode zu einem Kontext hinzugefügt wird.
class Program {
static void Main(string[] args) {
var student = new Student {
ID = 1001,
FirstMidName = "Wasim",
LastName = "Akram",
EnrollmentDate = DateTime.Parse( DateTime.Today.ToString())
};
using (var context = new MyContext()) {
context.Students.Add(student);
context.SaveChanges();
//// Display all Students from the database
var students = (from s in context.Students
orderby s.FirstMidName select s).ToList<Student>();
Console.WriteLine("Retrieve all Students from the database:");
foreach (var stdnt in students) {
string name = stdnt.FirstMidName + " " + stdnt.LastName;
Console.WriteLine("ID: {0}, Name: {1}", stdnt.ID, name);
}
Console.ReadKey();
}
}
}
Wenn der obige Code kompiliert und ausgeführt wird, erhalten Sie die folgende Ausgabe.
Retrieve all Students from the database:
ID: 1, Name: Edited Edited Edited Alain Bomer
ID: 2, Name: Edited Edited Edited Mark Upston
ID: 3, Name: Wasim Akram
LINQ zu Entitäten
Eines der wichtigsten Konzepte zum Verständnis von LINQ to Entities ist, dass es sich um eine deklarative Sprache handelt. Der Fokus liegt darauf, zu definieren, welche Informationen Sie benötigen, und nicht darauf, wie Sie die Informationen erhalten.
Dies bedeutet, dass Sie mehr Zeit mit der Arbeit mit Daten verbringen können und weniger Zeit damit verbringen müssen, den zugrunde liegenden Code herauszufinden, der für Aufgaben wie den Zugriff auf die Datenbank erforderlich ist.
Es ist wichtig zu verstehen, dass deklarative Sprachen dem Entwickler keine Kontrolle entziehen, aber es hilft dem Entwickler, die Aufmerksamkeit auf das Wesentliche zu lenken.
LINQ to Entities Wesentliche Schlüsselwörter
Es ist wichtig, die grundlegenden Schlüsselwörter zu kennen, die zum Erstellen einer LINQ-Abfrage verwendet werden. Es gibt nur wenige Schlüsselwörter, an die Sie sich erinnern müssen. Sie können sie jedoch auf verschiedene Arten kombinieren, um bestimmte Ergebnisse zu erzielen. Die folgende Liste enthält diese grundlegenden Schlüsselwörter und enthält eine einfache Beschreibung der einzelnen Schlüsselwörter.
Sr. Nr. | Schlüsselwort & Beschreibung |
---|---|
1 | Ascending Gibt an, dass eine Sortieroperation vom kleinsten (oder niedrigsten) Element eines Bereichs zum höchsten Element eines Bereichs stattfindet. Dies ist normalerweise die Standardeinstellung. Wenn Sie beispielsweise eine alphabetische Sortierung durchführen, liegt die Sortierung im Bereich von A bis Z. |
2 | By Gibt das Feld oder den Ausdruck an, mit dem eine Gruppierung implementiert wird. Das Feld oder der Ausdruck definiert einen Schlüssel, der zum Ausführen der Gruppierungsaufgabe verwendet wird. |
3 | Descending Gibt an, dass eine Sortieroperation vom größten (oder höchsten) Element eines Bereichs zum niedrigsten Element eines Bereichs stattfindet. Wenn Sie beispielsweise eine alphabetische Sortierung durchführen, liegt die Sortierung im Bereich von Z bis A. |
4 | Equals Wird zwischen der linken und der rechten Klausel einer Join-Anweisung verwendet, um die primäre Kontextdatenquelle mit der sekundären Kontextdatenquelle zu verbinden. Das Feld oder der Ausdruck links neben dem Schlüsselwort equals gibt die primäre Datenquelle an, während das Feld oder der Ausdruck rechts neben dem Schlüsselwort equals die sekundäre Datenquelle angibt. |
5 | From Gibt die Datenquelle an, die zum Abrufen der erforderlichen Informationen verwendet wird, und definiert eine Bereichsvariable. Diese Variable hat den gleichen Zweck wie eine Variable, die für die Iteration in einer Schleife verwendet wird. |
6 | Group Organisiert die Ausgabe anhand des von Ihnen angegebenen Schlüsselwerts in Gruppen. Verwenden Sie mehrere Gruppenklauseln, um mehrere Ebenen der Ausgabeorganisation zu erstellen. Die Reihenfolge der Gruppenklauseln bestimmt die Tiefe, in der ein bestimmter Schlüsselwert in der Gruppierungsreihenfolge angezeigt wird. Sie kombinieren dieses Schlüsselwort mit by, um einen bestimmten Kontext zu erstellen. |
7 | In Wird auf verschiedene Arten verwendet. In diesem Fall bestimmt das Schlüsselwort die kontextbezogene Datenbankquelle, die für eine Abfrage verwendet wird. Bei der Arbeit mit einem Join wird das Schlüsselwort in für jede kontextbezogene Datenbankquelle verwendet, die für den Join verwendet wird. |
8 | Into Gibt einen Bezeichner an, den Sie als Referenz für LINQ-Abfrageklauseln wie Join, Group und Select verwenden können. |
9 | Join Erstellt eine einzelne Datenquelle aus zwei verwandten Datenquellen, z. B. in einem Master- / Detail-Setup. Ein Join kann einen inneren, einen Gruppen- oder einen linken-äußeren Join angeben, wobei der innere Join die Standardeinstellung ist. Weitere Informationen zu Joins finden Sie unter msdn.microsoft.com |
10 | Let Definiert eine Bereichsvariable, mit der Sie Unterausdrucksergebnisse in einem Abfrageausdruck speichern können. In der Regel wird die Bereichsvariable verwendet, um eine zusätzliche Aufzählung bereitzustellen oder die Effizienz einer Abfrage zu erhöhen (sodass eine bestimmte Aufgabe, z. B. das Ermitteln des Kleinbuchstabenwerts einer Zeichenfolge, nicht mehr als einmal ausgeführt werden muss). |
11 | On Gibt das Feld oder den Ausdruck an, mit dem ein Join implementiert wird. Das Feld oder der Ausdruck definiert ein Element, das beiden kontextbezogenen Datenquellen gemeinsam ist. |
12 | Orderby Erstellt eine Sortierreihenfolge für die Abfrage. Sie können das Schlüsselwort aufsteigend oder absteigend hinzufügen, um die Reihenfolge der Sortierung zu steuern. Verwenden Sie mehrere orderby-Klauseln, um mehrere Sortierebenen zu erstellen. Die Reihenfolge der orderby-Klauseln bestimmt die Reihenfolge, in der die Sortierausdrücke behandelt werden. Die Verwendung einer anderen Reihenfolge führt also zu einer anderen Ausgabe. |
13 | Where Definiert, was LINQ aus der Datenquelle abrufen soll. Sie verwenden einen oder mehrere Boolesche Ausdrücke, um die Besonderheiten des Abrufs zu definieren. Die Booleschen Ausdrücke werden durch && (AND) und || voneinander getrennt (ODER) Operatoren. |
14 | Select Bestimmt die Ausgabe der LINQ-Abfrage, indem angegeben wird, welche Informationen zurückgegeben werden sollen. Diese Anweisung definiert den Datentyp der Elemente, die LINQ während des Iterationsprozesses zurückgibt. |
Projektion
Projektionsabfragen verbessern die Effizienz Ihrer Anwendung, indem nur bestimmte Felder aus Ihrer Datenbank abgerufen werden.
Sobald Sie die Daten haben, möchten Sie sie möglicherweise nach Bedarf projizieren oder filtern, um die Daten vor der Ausgabe zu formen.
Die Hauptaufgabe eines LINQ to Entities-Ausdrucks besteht darin, Daten abzurufen und als Ausgabe bereitzustellen.
Der Abschnitt „Entwickeln von LINQ to Entities-Abfragen“ in diesem Kapitel zeigt die Techniken zur Ausführung dieser grundlegenden Aufgabe.
Werfen wir einen Blick auf den folgenden Code, in dem die Liste der Schüler abgerufen wird.
using (var context = new UniContextEntities()) {
var studentList = from s in context.Students select s;
foreach (var student in studentList) {
string name = student.FirstMidName + " " + student.LastName;
Console.WriteLine("ID : {0}, Name: {1}", student.ID, name);
}
}
Einzelobjekt
Um ein einzelnes Schülerobjekt abzurufen, können Sie die Aufzählungsmethoden First () oder FirstOrDefault verwenden, die das erste Element einer Sequenz zurückgeben. Der Unterschied zwischen First und FirstOrDefault besteht darin, dass First () eine Ausnahme auslöst, wenn für die angegebenen Kriterien keine Ergebnisdaten vorhanden sind, während FirstOrDefault () den Standardwert null zurückgibt, wenn keine Ergebnisdaten vorhanden sind. Im folgenden Codefragment wird der erste Schüler aus der Liste abgerufen, dessen Vorname Ali ist.
using (var context = new UniContextEntities()) {
var student = (from s in context.Students where s.FirstMidName
== "Ali" select s).FirstOrDefault<Student>();
string name = student.FirstMidName + " " + student.LastName;
Console.WriteLine("ID : {0}, Name: {1}", student.ID, name);
}
Sie können auch Single () oder SingleOrDefault verwenden, um ein einzelnes Schülerobjekt abzurufen, das ein einzelnes, spezifisches Element einer Sequenz zurückgibt. Im folgenden Beispiel wird ein einzelner Schüler mit der ID 2 abgerufen.
using (var context = new UniContextEntities()) {
var student = (from s in context.Students where s.ID
== 2 select s).SingleOrDefault<Student>();
string name = student.FirstMidName + " " + student.LastName;
Console.WriteLine("ID : {0}, Name: {1}", student.ID, name);
Console.ReadKey();
}
Liste der Objekte
Wenn Sie die Liste der Schüler abrufen möchten, deren Vorname Ali ist, können Sie die Aufzählungsmethode ToList () verwenden.
using (var context = new UniContextEntities()) {
var studentList = (from s in context.Students where s.FirstMidName
== "Ali" select s).ToList();
foreach (var student in studentList) {
string name = student.FirstMidName + " " + student.LastName;
Console.WriteLine("ID : {0}, Name: {1}", student.ID, name);
}
Console.ReadKey();
}
Auftrag
Um Daten / Listen in einer bestimmten Reihenfolge abzurufen, können Sie das Schlüsselwort orderby verwenden. Im folgenden Code wird die Snippet-Liste des Schülers in aufsteigender Reihenfolge abgerufen.
using (var context = new UniContextEntities()) {
var studentList = (from s in context.Students orderby
s.FirstMidName ascending select s).ToList();
foreach (var student in studentList) {
string name = student.FirstMidName + " " + student.LastName;
Console.WriteLine("ID : {0}, Name: {1}", student.ID, name);
}
Console.ReadKey();
}
Standard Vs Projection Entity Framework-Abfrage
Angenommen, Sie haben ein Studentenmodell, das die ID, den Vornamen, den Nachnamen und das Registrierungsdatum enthält. Wenn Sie eine Liste der Schüler zurückgeben möchten, gibt eine Standardabfrage alle Felder zurück. Wenn Sie jedoch nur eine Liste der Schüler abrufen möchten, die die Felder ID, Vorname und Nachname enthalten. Hier sollten Sie eine Projektionsabfrage verwenden. Das folgende Beispiel zeigt eine einfache Projektionsabfrage.
using (var context = new UniContextEntities()) {
var studentList = from s in context.Students
orderby s.FirstMidName ascending
where s.FirstMidName == "Ali"
select new {s.ID, s.FirstMidName, s.LastName};
foreach (var student in studentList) {
string name = student.FirstMidName + " " + student.LastName;
Console.WriteLine("ID : {0}, Name: {1}", student.ID, name);
}
Console.ReadKey();
}
Die obige Projektionsabfrage schließt das Feld EnrollmentDate aus. Dadurch wird Ihre Bewerbung viel schneller.
In Entity Framework 6.0 wird eine neue Funktion eingeführt, die als bekannt ist Logging SQL. Während der Arbeit mit Entity Framework werden Befehle oder eine gleichwertige SQL-Abfrage an die Datenbank gesendet, um CRUD-Vorgänge (Erstellen, Lesen, Aktualisieren und Löschen) auszuführen.
Diese Funktion des Entity Framework besteht darin, eine von Entity Framework intern generierte äquivalente SQL-Abfrage zu erfassen und als Ausgabe bereitzustellen.
Vor Entity Framework 6 hatte der Entwickler keine andere Wahl, als Datenbankverfolgungsdienstprogramme oder Datenbankverfolgungstools von Drittanbietern zu verwenden, wenn Datenbankabfragen und -befehle nachverfolgt werden mussten.
In Entity Framework 6 bietet diese neue Funktion eine einfache Möglichkeit, indem alle von Entity Framework ausgeführten Vorgänge protokolliert werden.
Alle Aktivitäten, die von Entity Framework ausgeführt werden, werden mit DbContext.Database.Log protokolliert.
Schauen wir uns den folgenden Code an, in dem ein neuer Schüler zur Datenbank hinzugefügt wird.
class Program {
static void Main(string[] args) {
using (var context = new UniContextEntities()) {
context.Database.Log = Console.Write;
// Create a new student and save it
context.Students.Add(new Student {
FirstMidName = "Salman",
LastName = "Khan",
EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
});
context.SaveChanges();
Console.ReadKey();
}
}
}
Wenn der obige Code ausgeführt wird, erhalten Sie die folgende Ausgabe, die eigentlich das Protokoll aller von EF im obigen Code ausgeführten Aktivitäten ist.
Opened connection at 10/28/2015 6:27:35 PM +05:00
Started transaction at 10/28/2015 6:27:35 PM +05:00
INSERT [dbo].[Student]([LastName], [FirstMidName], [EnrollmentDate])
VALUES (@0, @1, @2)
SELECT [ID]
FROM [dbo].[Student]
WHERE @@ROWCOUNT > 0 AND [ID] = scope_identity()
-- @0: 'Khan' (Type = String, Size = -1)
-- @1: 'Salman' (Type = String, Size = -1)
-- @2: '10/28/2015 12:00:00 AM' (Type = DateTime)
-- Executing at 10/28/2015 6:27:35 PM +05:00
-- Completed in 5 ms with result: SqlDataReader
Committed transaction at 10/28/2015 6:27:35 PM +05:00
Closed connection at 10/28/2015 6:27:35 PM +05:00
Wenn die Log-Eigenschaft festgelegt ist, werden die folgenden Aktivitäten protokolliert:
SQL für alle Arten von Befehlen, z. B. Abfragen, einschließlich Einfügungen, Aktualisierungen und Löschungen, die im Rahmen von SaveChanges generiert wurden
Parameters
Gibt an, ob der Befehl asynchron ausgeführt wird
Ein Zeitstempel, der angibt, wann der Befehl ausgeführt wurde
Der Befehl wurde erfolgreich ausgeführt oder ist fehlgeschlagen
Einige Angaben zum Ergebniswert
Die ungefähre Zeit, die zum Ausführen des Befehls benötigt wurde
An einem anderen Ort anmelden
Wenn Sie bereits über ein Protokollierungsframework verfügen und eine Protokollierungsmethode definieren, können Sie diese auch an einem anderen Ort protokollieren.
Schauen wir uns das folgende Beispiel an, in dem wir eine andere Klasse MyLogger haben.
class Program {
static void Main(string[] args) {
using (var context = new UniContextEntities()) {
context.Database.Log = s ⇒ MyLogger.Log("EFLoggingDemo", s);
// Create a new student and save it
context.Students.Add(new Student {
FirstMidName = "Salman",
LastName = "Khan",
EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
});
context.SaveChanges();
Console.ReadKey();
}
}
}
public class MyLogger {
public static void Log(string application, string message) {
Console.WriteLine("Application: {0}, EF Message: {1} ",application, message);
}
}
Wir empfehlen, dass Sie das obige Beispiel zum besseren Verständnis Schritt für Schritt ausführen.
In Entity Framework 6.0 gibt es eine weitere neue Funktion namens Interceptoroder Abfangen. Der Interception-Code basiert auf dem Konzept voninterception interfaces. Beispielsweise definiert die IDbCommandInterceptor-Schnittstelle Methoden, die aufgerufen werden, bevor EF ExecuteNonQuery, ExecuteScalar, ExecuteReader und verwandte Methoden aufruft.
Entity Framework kann durch Abfangen wirklich glänzen. Mit diesem Ansatz können Sie viel mehr Informationen vorübergehend erfassen, ohne Ihren Code aufräumen zu müssen.
Um dies zu implementieren, müssen Sie Ihren eigenen benutzerdefinierten Interceptor erstellen und entsprechend registrieren.
Sobald eine Klasse erstellt wurde, die die IDbCommandInterceptor-Schnittstelle implementiert, kann sie mithilfe der DbInterception-Klasse bei Entity Framework registriert werden.
Die IDbCommandInterceptor-Schnittstelle verfügt über sechs Methoden, und Sie müssen alle diese Methoden implementieren. Im Folgenden finden Sie die grundlegende Implementierung dieser Methoden.
Schauen wir uns den folgenden Code an, in dem die IDbCommandInterceptor-Schnittstelle implementiert ist.
public class MyCommandInterceptor : IDbCommandInterceptor {
public static void Log(string comm, string message) {
Console.WriteLine("Intercepted: {0}, Command Text: {1} ", comm, message);
}
public void NonQueryExecuted(DbCommand command,
DbCommandInterceptionContext<int> interceptionContext) {
Log("NonQueryExecuted: ", command.CommandText);
}
public void NonQueryExecuting(DbCommand command,
DbCommandInterceptionContext<int> interceptionContext) {
Log("NonQueryExecuting: ", command.CommandText);
}
public void ReaderExecuted(DbCommand command,
DbCommandInterceptionContext<DbDataReader> interceptionContext) {
Log("ReaderExecuted: ", command.CommandText);
}
public void ReaderExecuting(DbCommand command,
DbCommandInterceptionContext<DbDataReader> interceptionContext) {
Log("ReaderExecuting: ", command.CommandText);
}
public void ScalarExecuted(DbCommand command,
DbCommandInterceptionContext<object> interceptionContext) {
Log("ScalarExecuted: ", command.CommandText);
}
public void ScalarExecuting(DbCommand command,
DbCommandInterceptionContext<object> interceptionContext) {
Log("ScalarExecuting: ", command.CommandText);
}
}
Abfangjäger registrieren
Sobald eine Klasse erstellt wurde, die eine oder mehrere der Interception-Schnittstellen implementiert, kann sie mithilfe der DbInterception-Klasse bei EF registriert werden, wie im folgenden Code gezeigt.
DbInterception.Add(new MyCommandInterceptor());
Interceptors können auch auf App-Domain-Ebene mithilfe der DbConfiguration-Code-basierten Konfiguration registriert werden, wie im folgenden Code gezeigt.
public class MyDBConfiguration : DbConfiguration {
public MyDBConfiguration() {
DbInterception.Add(new MyCommandInterceptor());
}
}
Sie können die Interceptor-Konfigurationsdatei auch mit dem folgenden Code konfigurieren:
<entityFramework>
<interceptors>
<interceptor type = "EFInterceptDemo.MyCommandInterceptor, EFInterceptDemo"/>
</interceptors>
</entityFramework>
Die räumliche Typunterstützung wurde in Entity Framework 5 eingeführt. Eine Reihe von Operatoren ist ebenfalls enthalten, damit Abfragen räumliche Daten analysieren können. Beispielsweise kann eine Abfrage basierend auf der Entfernung zwischen zwei geografischen Standorten filtern.
Mit Entity Framework können neue räumliche Datentypen als Eigenschaften für Ihre Klassen verfügbar gemacht und räumlichen Spalten in Ihrer Datenbank zugeordnet werden.
Sie können auch LINQ-Abfragen schreiben, die die räumlichen Operatoren zum Filtern, Sortieren und Gruppieren basierend auf in der Datenbank durchgeführten räumlichen Berechnungen verwenden.
Es gibt zwei hauptsächliche räumliche Datentypen:
Der Geografiedatentyp speichert ellipsoide Daten, z. B. GPS-Längen- und Breitengradkoordinaten.
Der Geometriedatentyp repräsentiert das euklidische (flache) Koordinatensystem.
Schauen wir uns das folgende Beispiel für Cricket Ground an.
Step 1 - Erstellen Sie ein neues Projekt über die Menüoption Datei → Neu → Projekt.
Step 2 - Wählen Sie im linken Bereich die Konsolenanwendung aus.
Step 3 - Klicken Sie mit der rechten Maustaste auf den Projektnamen und wählen Sie NuGet-Pakete verwalten…
Step 4 - Installieren Sie Entity Framework.
Step 5 - Fügen Sie einen Verweis auf die Assembly System.Data.Entity hinzu und fügen Sie die Anweisung System.Data.Spatial using für räumliche Datentypen hinzu.
Step 6 - Fügen Sie die folgende Klasse in die Datei Program.cs ein.
public class CricketGround {
public int ID { get; set; }
public string Name { get; set; }
public DbGeography Location { get; set; }
}
Step 7 - Zusätzlich zum Definieren von Entitäten müssen Sie eine Klasse definieren, die von DbContext abgeleitet ist und die Eigenschaften von DbSet <TEntity> verfügbar macht.
Fügen Sie in Program.cs die Kontextdefinition hinzu.
public partial class CricketGroundContext : DbContext {
public DbSet<CricketGround> CricketGrounds { get; set; }
}
Step 8 - Fügen Sie der Hauptfunktion den folgenden Code hinzu, wodurch dem Kontext zwei neue CricketGround-Objekte hinzugefügt werden.
class Program {
static void Main(string[] args) {
using (var context = new CricketGroundContext()) {
context.CricketGrounds.Add(new CricketGround() {
Name = "Shalimar Cricket Ground",
Location = DbGeography.FromText("POINT(-122.336106 47.605049)"),
});
context.CricketGrounds.Add(new CricketGround() {
Name = "Marghazar Stadium", Location = DbGeography
.FromText("POINT(-122.335197 47.646711)"),
});
context.SaveChanges();
var myLocation = DbGeography.FromText("POINT(-122.296623 47.640405)");
var cricketGround = (from cg in context.CricketGrounds
orderby cg.Location.Distance(myLocation) select cg).FirstOrDefault();
Console.WriteLine("The closest Cricket Ground to you is: {0}.", cricketGround.Name);
}
}
}
Die räumlichen Eigenschaften werden mithilfe der DbGeography.FromText-Methode initialisiert. Der als WellKnownText dargestellte geografische Punkt wird an die Methode übergeben und speichert dann die Daten. Danach wird das CricketGround-Objekt dort abgerufen, wo sein Standort dem angegebenen Standort am nächsten liegt.
Wenn der obige Code ausgeführt wird, erhalten Sie die folgende Ausgabe:
The closest Cricket Ground to you is: Marghazar Stadium
Wir empfehlen, dass Sie das obige Beispiel zum besseren Verständnis Schritt für Schritt ausführen.
Durch Vererbung können komplexe Modelle erstellt werden, die das Denken der Entwickler besser widerspiegeln und den Arbeitsaufwand für die Interaktion mit diesen Modellen verringern. Die mit Entitäten verwendete Vererbung dient demselben Zweck wie die mit Klassen verwendete Vererbung, sodass Entwickler bereits die Grundlagen der Funktionsweise dieser Funktion kennen.
Schauen wir uns das folgende Beispiel an und erstellen ein neues Konsolenanwendungsprojekt.
Step 1 - Fügen Sie das ADO.NET-Entitätsdatenmodell hinzu, indem Sie mit der rechten Maustaste auf den Projektnamen klicken und Hinzufügen → Neues Element auswählen.
Step 2 - Fügen Sie eine Entität hinzu und nennen Sie sie Person, indem Sie alle im Kapitel Modell erster Ansatz genannten Schritte ausführen.
Step 3 - Fügen Sie einige skalare Eigenschaften hinzu, wie in der folgenden Abbildung gezeigt.
Step 4 - Wir werden zwei weitere Entitäten hinzufügen Student und Teacher, die die Eigenschaften von Personentabelle erben.
Step 5 - Fügen Sie nun die Entität Student hinzu und wählen Sie Person aus der Combobox vom Basistyp aus, wie in der folgenden Abbildung gezeigt.
Step 6 - Fügen Sie auf ähnliche Weise eine Lehrerentität hinzu.
Step 7 - Fügen Sie nun der Schülerentität die skalare Eigenschaft EnrollmentDate und der Lehrerentität die Eigenschaft HireDate hinzu.
Step 8 - Lassen Sie uns fortfahren und die Datenbank generieren.
Step 9 - Klicken Sie mit der rechten Maustaste auf die Entwurfsoberfläche und wählen Sie Datenbank aus Modell generieren…
Step 10- Um eine neue Datenbank zu erstellen, klicken Sie auf Neue Verbindung… Der folgende Dialog wird geöffnet. OK klicken.
Step 11- Klicken Sie auf Fertig stellen. Dadurch wird dem Projekt die Datei * .edmx.sql hinzugefügt. Sie können DDL-Skripte in Visual Studio ausführen, indem Sie die SQL-Datei öffnen. Klicken Sie nun mit der rechten Maustaste und wählen Sie Ausführen.
Step 12 - Gehen Sie zum Server-Explorer. Sie werden sehen, dass die Datenbank mit drei angegebenen Tabellen erstellt wurde.
Step 13 - Sie können auch sehen, dass die folgenden Domänenklassen ebenfalls automatisch generiert werden.
public partial class Person {
public int ID { get; set; }
public string FirstMidName { get; set; }
public string LastName { get; set; }
}
public partial class Student : Person {
public System.DateTime EnrollmentDate { get; set; }
}
public partial class Teacher : Person {
public System.DateTime HireDate { get; set; }
}
Es folgt die Context-Klasse.
public partial class InheritanceModelContainer : DbContext {
public InheritanceModelContainer() :
base("name = InheritanceModelContainer") {}
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<Person> People { get; set; }
}
Fügen wir der Datenbank einige Schüler und Lehrer hinzu und rufen sie dann aus der Datenbank ab.
class Program {
static void Main(string[] args) {
using (var context = new InheritanceModelContainer()) {
var student = new Student {
FirstMidName = "Meredith",
LastName = "Alonso",
EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
};
context.People.Add(student);
var student1 = new Student {
FirstMidName = "Arturo",
LastName = "Anand",
EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
};
context.People.Add(student1);
var techaer = new Teacher {
FirstMidName = "Peggy",
LastName = "Justice",
HireDate = DateTime.Parse(DateTime.Today.ToString())
};
context.People.Add(techaer);
var techaer1 = new Teacher {
FirstMidName = "Yan",
LastName = "Li",
HireDate = DateTime.Parse(DateTime.Today.ToString())
};
context.People.Add(techaer1);
context.SaveChanges();
}
}
}
Schüler und Lehrer werden in die Datenbank aufgenommen. Um Schüler und Lehrer zu finden, dieOfType Es muss eine Methode verwendet werden, die Schüler und Lehrer in Bezug auf die angegebene Abteilung zurückgibt.
Console.WriteLine("All students in database");
Console.WriteLine("");
foreach (var student in context.People.OfType<Student>()) {
string name = student.FirstMidName + " " + student.LastName;
Console.WriteLine("ID: {0}, Name: {1}, \tEnrollment Date {2} ",
student.ID, name, student.EnrollmentDate.ToString());
}
Console.WriteLine("");
Console.WriteLine("************************************************************ *****");
Console.WriteLine("");
Console.WriteLine("All teachers in database");
Console.WriteLine("");
foreach (var teacher in context.People.OfType<Teacher>()) {
string name = teacher.FirstMidName + " " + teacher.LastName;
Console.WriteLine("ID: {0}, Name: {1}, \tHireDate {2} ",
teacher.ID, name, teacher.HireDate.ToString());
}
Console.WriteLine("");
Console.WriteLine("************************************************************ *****");
Console.ReadKey();
Wenn Sie in der ersten Abfrage OfType <Student> () verwenden, können Sie nicht auf HireDate zugreifen, da die HireDate-Eigenschaft Teil der Lehrerentität ist und auf die EnrollmentDate-Eigenschaft ebenfalls nicht zugegriffen werden kann, wenn Sie OfType <Teacher> () verwenden.
Wenn der obige Code ausgeführt wird, erhalten Sie die folgende Ausgabe:
All students in database
ID: 1, Name: Meredith Alonso, Enrollment Date 10/30/2015 12:00:00 AM
ID: 2, Name: Arturo Anand, Enrollment Date 10/30/2015 12:00:00 AM
*****************************************************************
All teachers in database
ID: 3, Name: Peggy Justice, HireDate 10/30/2015 12:00:00 AM
ID: 4, Name: Yan Li, HireDate 10/30/2015 12:00:00 AM
*****************************************************************
Wir empfehlen, dass Sie das obige Beispiel zum besseren Verständnis Schritt für Schritt ausführen.
In Entity Framework 5 und früheren Versionen von Entity Framework wurde der Code zwischen Kernbibliotheken (hauptsächlich System.Data.Entity.dll) aufgeteilt, die als Teil von .NET Framework ausgeliefert wurden, und die zusätzlichen Bibliotheken (hauptsächlich EntityFramework.dll) wurden verteilt und verteilt Lieferung mit NuGet wie in der folgenden Abbildung gezeigt.
In Entity Framework 6 werden die Kern-APIs, die zuvor Teil von .NET Framework waren, auch als Teil des NuGet-Pakets ausgeliefert und verteilt.
Dies war notwendig, damit Entity Framework Open Source werden konnte. Infolgedessen müssen Anwendungen jedoch immer dann neu erstellt werden, wenn Ihre Anwendung von älteren Versionen von Entity Framework auf EF 6 migriert oder aktualisiert werden muss.
Der Migrationsprozess ist unkompliziert, wenn Ihre Anwendung DbContext verwendet, das in EF 4.1 und höher ausgeliefert wurde. Wenn Ihre Anwendung jedoch ObjectContext ist, ist wenig mehr Arbeit erforderlich.
Schauen wir uns die folgenden Schritte an, die Sie ausführen müssen, um eine vorhandene Anwendung auf EF6 zu aktualisieren.
Step 1 - Der erste Schritt besteht darin, auf .NET Framework 4.5.2 zu zielen und später mit der rechten Maustaste auf Ihr Projekt zu klicken und Eigenschaften auszuwählen.
Step 2 - Klicken Sie erneut mit der rechten Maustaste auf Ihr Projekt und wählen Sie NuGet-Pakete verwalten ...
Step 3- Wählen Sie auf der Registerkarte Online die Option EntityFramework aus und klicken Sie auf Installieren. Stellen Sie sicher, dass Assemblyverweise auf System.Data.Entity.dll entfernt werden.
Wenn Sie das EF6 NuGet-Paket installieren, sollten automatisch alle Verweise auf System.Data.Entity für Sie aus Ihrem Projekt entfernt werden.
Step 4 - Wenn Sie ein Modell haben, das mit dem EF Designer erstellt wurde, müssen Sie auch die Codegenerierungsvorlagen aktualisieren, um EF6-kompatiblen Code zu generieren.
Step 5 - Löschen Sie in Ihrem Projektmappen-Explorer unter Ihrer edmx-Datei vorhandene Vorlagen zur Codegenerierung, die normalerweise die Namen <edmx_Dateiname> .tt und <edmx_Dateiname> .Context.tt tragen.
Step 6 - Öffnen Sie Ihr Modell im EF Designer, klicken Sie mit der rechten Maustaste auf die Entwurfsoberfläche und wählen Sie Codegenerierungselement hinzufügen ...
Step 7 - Fügen Sie die entsprechende EF 6.x-Codegenerierungsvorlage hinzu.
Außerdem wird automatisch EF6-kompatibler Code generiert.
Wenn Ihre Anwendungen EF 4.1 oder höher verwenden, müssen Sie nichts am Code ändern, da sich die Namespaces für DbContext- und Code First-Typen nicht geändert haben.
Wenn Ihre Anwendung jedoch eine ältere Version von Entity Framework verwendet, wurden Typen wie ObjectContext, die zuvor in System.Data.Entity.dll enthalten waren, in neue Namespaces verschoben.
Step 8 - Sie müssen Ihre Verwendungs- oder Importanweisungen aktualisieren, um gegen EF6 zu erstellen.
Die allgemeine Regel für Namespace-Änderungen lautet, dass jeder Typ in System.Data. * Nach System.Data.Entity.Core. * Verschoben wird. Es folgen einige davon -
- System.Data.EntityException ⇒ System.Data.Entity.Core.EntityException
- System.Data.Objects.ObjectContext ⇒ System.Data.Entity.Core.Objects.ObjectContext;
- System.Data.Objects.DataClasses.RelationshipManager ⇒ System.Data.Entity.Core.Objects.DataClasses.RelationshipManager;
Einige Typen befinden sich in den Core- Namespaces, da sie für die meisten DbContext-basierten Anwendungen nicht direkt verwendet werden.
- System.Data.EntityState ⇒ System.Data.Entity.EntityState
- System.Data.Objects.DataClasses.EdmFunctionAttribute ⇒ System.Data.Entity.DbFunctionAttribute
Ihr vorhandenes Entity Framework-Projekt funktioniert in Entity Framework 6.0 ohne größere Änderungen.
Eifriges Laden ist der Prozess, bei dem eine Abfrage für einen Entitätstyp auch verwandte Entitäten als Teil der Abfrage lädt. Eifriges Laden wird durch die Verwendung von erreichtInclude method.
Dies bedeutet, dass das Anfordern verwandter Daten zusammen mit den Abfrageergebnissen aus der Datenbank zurückgegeben wird. Es wird nur eine Verbindung zur Datenquelle hergestellt. Bei der ersten Abfrage wird eine größere Datenmenge zurückgegeben.
Wenn Sie beispielsweise Schüler befragen, laden Sie ihre Einschreibungen eifrig. Die Schüler und ihre Einschreibungen werden in einer einzigen Abfrage abgerufen.
Schauen wir uns das folgende Beispiel an, in dem alle Schüler mit ihren jeweiligen Einschreibungen durch eifriges Laden aus der Datenbank abgerufen werden.
class Program {
static void Main(string[] args) {
using (var context = new UniContextEntities()) {
// Load all students and related enrollments
var students = context.Students
.Include(s ⇒ s.Enrollments).ToList();
foreach (var student in students) {
string name = student.FirstMidName + " " + student.LastName;
Console.WriteLine("ID: {0}, Name: {1}", student.ID, name);
foreach (var enrollment in student.Enrollments) {
Console.WriteLine("Enrollment ID: {0}, Course ID: {1}",
enrollment.EnrollmentID, enrollment.CourseID);
}
}
Console.ReadKey();
}
}
}
Wenn der obige Code kompiliert und ausgeführt wird, erhalten Sie die folgende Ausgabe.
ID: 1, Name: Ali Alexander
Enrollment ID: 1, Course ID: 1050
Enrollment ID: 2, Course ID: 4022
Enrollment ID: 3, Course ID: 4041
ID: 2, Name: Meredith Alonso
Enrollment ID: 4, Course ID: 1045
Enrollment ID: 5, Course ID: 3141
Enrollment ID: 6, Course ID: 2021
ID: 3, Name: Arturo Anand
Enrollment ID: 7, Course ID: 1050
ID: 4, Name: Gytis Barzdukas
Enrollment ID: 8, Course ID: 1050
Enrollment ID: 9, Course ID: 4022
Im Folgenden finden Sie einige der anderen Formen von eifrigen Ladeanfragen, die verwendet werden können.
// Load one Student and its related enrollments
var student1 = context.Students
.Where(s ⇒ s.FirstMidName == "Ali")
.Include(s ⇒ s.Enrollments).FirstOrDefault();
// Load all Students and related enrollments
// using a string to specify the relationship
var studentList = context.Students
.Include("Enrollments").ToList();
// Load one Student and its related enrollments
// using a string to specify the relationship
var student = context.Students
.Where(s ⇒ s.FirstMidName == "Salman")
.Include("Enrollments").FirstOrDefault();
Mehrere Ebenen
Es ist auch möglich, mehrere Ebenen verwandter Entitäten eifrig zu laden. Die folgenden Abfragen zeigen Beispiele für Studenten, Einschreibungen und Kurse.
// Load all Students, all related enrollments, and all related courses
var studentList = context.Students
.Include(s ⇒ s.Enrollments.Select(c ⇒ c.Course)).ToList();
// Load all Students, all related enrollments, and all related courses
// using a string to specify the relationships
var students = context.Students
.Include("Enrollments.Course").ToList();
Wir empfehlen, dass Sie das obige Beispiel zum besseren Verständnis Schritt für Schritt ausführen.
Das verzögerte Laden ist der Prozess, bei dem eine Entität oder eine Sammlung von Entitäten automatisch aus der Datenbank geladen wird, wenn zum ersten Mal auf eine Eigenschaft zugegriffen wird, die sich auf die Entität / Entitäten bezieht. Lazy Loading bedeutet, das Laden verwandter Daten zu verzögern, bis Sie dies ausdrücklich anfordern.
Bei Verwendung von POCO-Entitätstypen wird ein verzögertes Laden erreicht, indem Instanzen abgeleiteter Proxy-Typen erstellt und anschließend virtuelle Eigenschaften überschrieben werden, um den Lade-Hook hinzuzufügen.
Lazy Loading ist so ziemlich die Standardeinstellung.
Wenn Sie die Standardkonfiguration beibehalten und Entity Framework in Ihrer Abfrage nicht explizit mitteilen, dass Sie etwas anderes als verzögertes Laden möchten, erhalten Sie verzögertes Laden.
Wenn Sie beispielsweise die Entitätsklasse "Student" verwenden, werden die zugehörigen Registrierungen beim ersten Zugriff auf die Navigationseigenschaft "Registrierungen" geladen.
Die Navigationseigenschaft sollte als öffentlich und virtuell definiert werden. Kontext wirdNOT Führen Sie ein verzögertes Laden durch, wenn die Eigenschaft nicht als virtuell definiert ist.
Es folgt eine Student-Klasse, die die Navigationseigenschaft von Enrollments enthält.
public partial class Student {
public Student() {
this.Enrollments = new HashSet<Enrollment>();
}
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public System.DateTime EnrollmentDate { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
Schauen wir uns ein einfaches Beispiel an, in dem die Schülerliste zuerst aus der Datenbank geladen wird und dann die Einschreibungen eines bestimmten Schülers geladen werden, wann immer Sie sie benötigen.
class Program {
static void Main(string[] args) {
using (var context = new UniContextEntities()) {
//Loading students only
IList<Student> students = context.Students.ToList<Student>();
foreach (var student in students) {
string name = student.FirstMidName + " " + student.LastName;
Console.WriteLine("ID: {0}, Name: {1}", student.ID, name);
foreach (var enrollment in student.Enrollments) {
Console.WriteLine("Enrollment ID: {0}, Course ID: {1}",
enrollment.EnrollmentID, enrollment.CourseID);
}
}
Console.ReadKey();
}
}
}
Wenn der obige Code kompiliert und ausgeführt wird, erhalten Sie die folgende Ausgabe.
ID: 1, Name: Ali Alexander
Enrollment ID: 1, Course ID: 1050
Enrollment ID: 2, Course ID: 4022
Enrollment ID: 3, Course ID: 4041
ID: 2, Name: Meredith Alonso
Enrollment ID: 4, Course ID: 1045
Enrollment ID: 5, Course ID: 3141
Enrollment ID: 6, Course ID: 2021
ID: 3, Name: Arturo Anand
Enrollment ID: 7, Course ID: 1050
ID: 4, Name: Gytis Barzdukas
Enrollment ID: 8, Course ID: 1050
Enrollment ID: 9, Course ID: 4022
ID: 5, Name: Yan Li
Enrollment ID: 10, Course ID: 4041
ID: 6, Name: Peggy Justice
Enrollment ID: 11, Course ID: 1045
ID: 7, Name: Laura Norman
Enrollment ID: 12, Course ID: 3141
Lazy Loading ausschalten
Das verzögerte Laden und Serialisieren passt nicht gut zusammen. Wenn Sie nicht aufpassen, können Sie am Ende Ihre gesamte Datenbank abfragen, nur weil das verzögerte Laden aktiviert ist. Es wird empfohlen, das verzögerte Laden zu deaktivieren, bevor Sie eine Entität serialisieren.
Deaktivieren für bestimmte Navigationseigenschaften
Das verzögerte Laden der Enrollments-Auflistung kann deaktiviert werden, indem die Enrollments-Eigenschaft nicht virtuell gemacht wird, wie im folgenden Beispiel gezeigt.
public partial class Student {
public Student() {
this.Enrollments = new HashSet<Enrollment>();
}
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public System.DateTime EnrollmentDate { get; set; }
public ICollection<Enrollment> Enrollments { get; set; }
}
Für alle Entitäten deaktivieren
Das verzögerte Laden kann für alle Entitäten im Kontext deaktiviert werden, indem ein Flag für die Configuration-Eigenschaft auf false gesetzt wird, wie im folgenden Beispiel gezeigt.
public partial class UniContextEntities : DbContext {
public UniContextEntities(): base("name=UniContextEntities") {
this.Configuration.LazyLoadingEnabled = false;
}
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
throw new UnintentionalCodeFirstException();
}
}
Nachdem Sie das verzögerte Laden deaktiviert haben, werden Sie beim erneuten Ausführen des obigen Beispiels feststellen, dass die Anmeldungen nicht geladen werden und nur Schülerdaten abgerufen werden.
ID: 1, Name: Ali Alexander
ID: 2, Name: Meredith Alons
ID: 3, Name: Arturo Anand
ID: 4, Name: Gytis Barzduka
ID: 5, Name: Yan Li
ID: 6, Name: Peggy Justice
ID: 7, Name: Laura Norman
ID: 8, Name: Nino Olivetto
Wir empfehlen Ihnen, das obige Beispiel zum besseren Verständnis Schritt für Schritt auszuführen.
Wenn Sie das verzögerte Laden deaktiviert haben, ist es weiterhin möglich, verwandte Entitäten träge zu laden, dies muss jedoch mit einem expliziten Aufruf erfolgen.
Im Gegensatz zum verzögerten Laden besteht keine Unklarheit oder Verwechslungsgefahr hinsichtlich der Ausführung einer Abfrage.
Dazu verwenden Sie die Load-Methode für den Eintrag der zugehörigen Entität.
Rufen Sie für eine Eins-zu-Viele-Beziehung die Load-Methode für Collection auf.
Rufen Sie für eine Eins-zu-Eins-Beziehung die Load-Methode in Reference auf.
Schauen wir uns das folgende Beispiel an, in dem das verzögerte Laden deaktiviert ist und dann der Schüler mit dem Vornamen Ali abgerufen wird.
Die Schülerinformationen werden dann auf die Konsole geschrieben. Wenn Sie sich den Code ansehen, werden auch Registrierungsinformationen geschrieben, aber die Entität "Registrierungen" ist noch nicht geladen, sodass jede Schleife nicht ausgeführt wird.
Nachdem diese Entität "Registrierungen" explizit geladen wurde, werden Schülerinformationen und Registrierungsinformationen in das Konsolenfenster geschrieben.
class Program {
static void Main(string[] args) {
using (var context = new UniContextEntities()) {
context.Configuration.LazyLoadingEnabled = false;
var student = (from s in context.Students where s.FirstMidName ==
"Ali" select s).FirstOrDefault<Student>();
string name = student.FirstMidName + " " + student.LastName;
Console.WriteLine("ID: {0}, Name: {1}", student.ID, name);
foreach (var enrollment in student.Enrollments) {
Console.WriteLine("Enrollment ID: {0}, Course ID: {1}",
enrollment.EnrollmentID, enrollment.CourseID);
}
Console.WriteLine();
Console.WriteLine("Explicitly loaded Enrollments");
Console.WriteLine();
context.Entry(student).Collection(s ⇒ s.Enrollments).Load();
Console.WriteLine("ID: {0}, Name: {1}", student.ID, name);
foreach (var enrollment in student.Enrollments) {
Console.WriteLine("Enrollment ID: {0}, Course ID: {1}",
enrollment.EnrollmentID, enrollment.CourseID);
}
Console.ReadKey();
}
}
}
Wenn das obige Beispiel ausgeführt wird, erhalten Sie die folgende Ausgabe. Zunächst werden nur Schülerinformationen angezeigt, und nach dem expliziten Laden der Anmeldeentität werden sowohl die Schüler- als auch die zugehörigen Anmeldeinformationen angezeigt.
ID: 1, Name: Ali Alexander
Explicitly loaded Enrollments
ID: 1, Name: Ali Alexander
Enrollment ID: 1, Course ID: 1050
Enrollment ID: 2, Course ID: 4022
Enrollment ID: 3, Course ID: 4041
Wir empfehlen, dass Sie das obige Beispiel zum besseren Verständnis Schritt für Schritt ausführen.
In diesem Kapitel lernen Sie die Validierungstechniken kennen, die in ADO.NET Entity Framework zum Validieren der Modelldaten verwendet werden können. Entity Framework bietet eine Vielzahl von Validierungsfunktionen, die für die clientseitige Validierung auf einer Benutzeroberfläche implementiert oder für die serverseitige Validierung verwendet werden können.
In Entity Framework ist die Datenvalidierung Teil der Lösung zum Abfangen fehlerhafter Daten in einer Anwendung.
Entity Framework überprüft standardmäßig alle Daten, bevor sie in die Datenbank geschrieben werden, mithilfe einer Vielzahl von Datenüberprüfungsmethoden.
Entity Framework wird jedoch nach der Validierung der Benutzeroberflächendaten bereitgestellt. In diesem Fall ist eine Entitätsüberprüfung erforderlich, um alle von EF ausgelösten Ausnahmen zu behandeln und eine generische Nachricht anzuzeigen.
Es gibt einige Techniken zur Datenüberprüfung, um Ihre Fehlerprüfung zu verbessern und Fehlermeldungen an den Benutzer zurückzugeben.
DbContext verfügt über eine überschreibbare Methode namens ValidateEntity. Wenn Sie SaveChanges aufrufen, ruft Entity Framework diese Methode für jede Entität in ihrem Cache auf, deren Status nicht unverändert ist. Sie können die Validierungslogik hier direkt einfügen, wie im folgenden Beispiel für die Studentenentität gezeigt.
public partial class UniContextEntities : DbContext {
protected override System.Data.Entity.Validation
.DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry,
System.Collections.Generic.IDictionary<object, object> items) {
if (entityEntry.Entity is Student) {
if (entityEntry.CurrentValues.GetValue<string>("FirstMidName") == "") {
var list = new List<System.Data.Entity
.Validation.DbValidationError>();
list.Add(new System.Data.Entity.Validation
.DbValidationError("FirstMidName", "FirstMidName is required"));
return new System.Data.Entity.Validation
.DbEntityValidationResult(entityEntry, list);
}
}
if (entityEntry.CurrentValues.GetValue<string>("LastName") == "") {
var list = new List<System.Data.Entity
.Validation.DbValidationError>();
list.Add(new System.Data.Entity.Validation
.DbValidationError("LastName", "LastName is required"));
return new System.Data.Entity.Validation
.DbEntityValidationResult(entityEntry, list);
}
return base.ValidateEntity(entityEntry, items);
}
}
In der obigen ValidateEntity-Methode werden die Eigenschaften FirstMidName und LastName der Schülerentität überprüft. Wenn eine dieser Eigenschaften eine leere Zeichenfolge enthält, wird eine Fehlermeldung zurückgegeben.
Schauen wir uns ein einfaches Beispiel an, in dem ein neuer Schüler erstellt wird, der Vorname des Schülers jedoch eine leere Zeichenfolge ist, wie im folgenden Code gezeigt.
class Program {
static void Main(string[] args) {
using (var context = new UniContextEntities()) {
Console.WriteLine("Adding new Student to the database");
Console.WriteLine();
try {
context.Students.Add(new Student() {
FirstMidName = "",
LastName = "Upston"
});
context.SaveChanges();
} catch (DbEntityValidationException dbValidationEx) {
foreach (DbEntityValidationResult entityErr in
dbValidationEx.EntityValidationErrors) {
foreach (DbValidationError error in entityErr.ValidationErrors) {
Console.WriteLine("Error: {0}",error.ErrorMessage);
}
}
}
Console.ReadKey();
}
}
}
Wenn das obige Beispiel kompiliert und ausgeführt wird, wird im Konsolenfenster die folgende Fehlermeldung angezeigt.
Adding new Student to the database
Error: FirstMidName is required
Wir empfehlen Ihnen, das obige Beispiel zum besseren Verständnis Schritt für Schritt auszuführen.
Entity Framework bietet die Möglichkeit, die an Entitäten und ihren Beziehungen vorgenommenen Änderungen zu verfolgen, sodass beim Aufrufen der Kontextmethode SaveChanges die richtigen Aktualisierungen in der Datenbank vorgenommen werden. Dies ist eine wichtige Funktion des Entity Framework.
Die Änderungsverfolgung verfolgt Änderungen, während der Entitätssammlung neue Datensätze hinzugefügt und vorhandene Entitäten geändert oder entfernt werden.
Dann werden alle Änderungen von der DbContext-Ebene beibehalten.
Diese Spuränderungen gehen verloren, wenn sie nicht gespeichert werden, bevor das DbContext-Objekt zerstört wird.
Die DbChangeTracker-Klasse bietet Ihnen alle Informationen zu aktuellen Entitäten, die vom Kontext verfolgt werden.
Um eine Entität anhand des Kontexts zu verfolgen, muss sie über die Primärschlüsseleigenschaft verfügen.
In Entity Framework ist die Änderungsverfolgung standardmäßig aktiviert. Sie können die Änderungsverfolgung auch deaktivieren, indem Sie die AutoDetectChangesEnabled-Eigenschaft von DbContext auf false setzen. Wenn diese Eigenschaft auf true festgelegt ist, behält das Entity Framework den Status von Entitäten bei.
using (var context = new UniContextEntities()) {
context.Configuration.AutoDetectChangesEnabled = true;
}
Schauen wir uns das folgende Beispiel an, in dem die Schüler und ihre Einschreibungen aus der Datenbank abgerufen werden.
class Program {
static void Main(string[] args) {
using (var context = new UniContextEntities()) {
context.Configuration.AutoDetectChangesEnabled = true;
Console.WriteLine("Retrieve Student");
var student = (from s in context.Students where s.FirstMidName ==
"Ali" select s).FirstOrDefault<Student>();
string name = student.FirstMidName + " " + student.LastName;
Console.WriteLine("ID: {0}, Name: {1}", student.ID, name);
Console.WriteLine();
Console.WriteLine("Retrieve all related enrollments");
foreach (var enrollment in student.Enrollments) {
Console.WriteLine("Enrollment ID: {0}, Course ID: {1}",
enrollment.EnrollmentID, enrollment.CourseID);
}
Console.WriteLine();
Console.WriteLine("Context tracking changes of {0} entity.",
context.ChangeTracker.Entries().Count());
var entries = context.ChangeTracker.Entries();
foreach (var entry in entries) {
Console.WriteLine("Entity Name: {0}", entry.Entity.GetType().Name);
Console.WriteLine("Status: {0}", entry.State);
}
Console.ReadKey();
}
}
}
Wenn das obige Beispiel kompiliert und ausgeführt wird, erhalten Sie die folgende Ausgabe.
Retrieve Student
ID: 1, Name: Ali Alexander
Retrieve all related enrollments
Enrollment ID: 1, Course ID: 1050
Enrollment ID: 2, Course ID: 4022
Enrollment ID: 3, Course ID: 4041
Context tracking changes of 4 entity.
Entity Name: Student
Status: Unchanged
Entity Name: Enrollment
Status: Unchanged
Entity Name: Enrollment
Status: Unchanged
Entity Name: Enrollment
Status: Unchanged
Sie können sehen, dass alle Daten nur aus der Datenbank abgerufen werden, weshalb der Status für alle Entitäten unverändert bleibt.
Schauen wir uns nun ein weiteres einfaches Beispiel an, in dem wir eine weitere Einschreibung hinzufügen und einen Schüler aus der Datenbank löschen. Im Folgenden finden Sie den Code, in dem eine neue Registrierung hinzugefügt und ein Schüler gelöscht wird.
class Program {
static void Main(string[] args) {
using (var context = new UniContextEntities()) {
context.Configuration.AutoDetectChangesEnabled = true;
Enrollment enr = new Enrollment() {
StudentID = 1, CourseID = 3141
};
Console.WriteLine("Adding New Enrollment");
context.Enrollments.Add(enr);
Console.WriteLine("Delete Student");
var student = (from s in context.Students where s.ID ==
23 select s).SingleOrDefault<Student>();
context.Students.Remove(student);
Console.WriteLine("");
Console.WriteLine("Context tracking changes of {0} entity.",
context.ChangeTracker.Entries().Count());
var entries = context.ChangeTracker.Entries();
foreach (var entry in entries) {
Console.WriteLine("Entity Name: {0}", entry.Entity.GetType().Name);
Console.WriteLine("Status: {0}", entry.State);
}
Console.ReadKey();
}
}
}
Wenn das obige Beispiel kompiliert und ausgeführt wird, erhalten Sie die folgende Ausgabe.
Adding New Enrollment
Delete Student
Context tracking changes of 2 entity.
Entity Name: Enrollment
Status: Added
Entity Name: Student
Status: Deleted
Sie können jetzt sehen, dass der Status der Registrierungsentität auf "Hinzufügen" gesetzt und der Status der Studentenentität gelöscht wird, da eine neue Registrierung hinzugefügt wurde und ein Student aus der Datenbank entfernt wurde.
Wir empfehlen, dass Sie das obige Beispiel zum besseren Verständnis Schritt für Schritt ausführen.
In Entity Framework geht es bei Coloured Entity hauptsächlich darum, die Farbe der Entität im Designer so zu ändern, dass Entwickler verwandte Gruppen von Entitäten im Visual Studio-Designer leicht identifizieren können. Diese Funktion wurde erstmals in Entity Framework 5.0 eingeführt.
Diese Funktion hat nichts mit Leistungsaspekten zu tun.
Wenn Sie ein großes Projekt und viele Entitäten in einer edmx-Datei haben, ist diese Funktion sehr hilfreich, um Ihre Entitäten in verschiedene Module zu trennen.
Wenn Sie mit einer edmx-Datei arbeiten und diese im Designer geöffnet haben, wählen Sie zum Ändern der Farbe eine Entität in den Entwurfsfenstern aus. Klicken Sie dann mit der rechten Maustaste und wählen Sie Eigenschaften.
Wählen Sie im Eigenschaftenfenster die Eigenschaft Füllfarbe aus.
Geben Sie die Farbe entweder mit einem gültigen Farbnamen an, z. B. Grün oder einem gültigen RGB (255, 128, 128), oder Sie können auch aus der Farbauswahl auswählen.
Um die Farbe mehrerer Objekte auf einmal zu ändern, wählen Sie mehrere Objekte aus und ändern Sie die Füllfarbe für alle Objekte im Eigenschaftenfenster.
Sie können das Format der Eigenschaften auch ändern, indem Sie eine der folgenden Optionen auswählen:
- Anzeigename
- Anzeigename und -typ
Standardmäßig ist die Option Anzeigename ausgewählt. Um das Eigenschaftsformat zu ändern, klicken Sie mit der rechten Maustaste auf das Designerfenster.
Wählen Sie Skalareigenschaftsformat → Anzeigename und -typ.
Sie können jetzt sehen, dass der Typ auch zusammen mit dem Namen angezeigt wird.
Das Entity Framework bietet drei Ansätze zum Erstellen eines Entity-Modells, von denen jedes seine eigenen Vor- und Nachteile hat.
- Code zuerst
- Datenbank zuerst
- Modell zuerst
In diesem Kapitel werden wir kurz den ersten Code-Ansatz beschreiben. Einige Entwickler arbeiten lieber mit dem Designer in Code, während andere lieber nur mit ihrem Code arbeiten. Für diese Entwickler verfügt Entity Framework über einen Modellierungsworkflow, der als Code First bezeichnet wird.
Der Code First-Modellierungsworkflow zielt auf eine nicht vorhandene Datenbank ab und wird von Code First erstellt.
Es kann auch verwendet werden, wenn Sie eine leere Datenbank haben und Code First dann neue Tabellen hinzufügt.
Mit Code First können Sie Ihr Modell mithilfe von C # - oder VB.Net-Klassen definieren.
Zusätzliche Konfigurationen können optional mithilfe von Attributen für Ihre Klassen und Eigenschaften oder mithilfe einer fließenden API durchgeführt werden.
Warum zuerst Code?
Code First besteht wirklich aus einer Reihe von Puzzleteilen. Zuerst sind Ihre Domain-Klassen.
Die Domänenklassen haben nichts mit Entity Framework zu tun. Sie sind nur die Elemente Ihrer Geschäftsdomäne.
Entity Framework verfügt also über einen Kontext, der die Interaktion zwischen diesen Klassen und Ihrer Datenbank verwaltet.
Der Kontext ist nicht spezifisch für Code First. Es ist eine Entity Framework-Funktion.
Code First fügt einen Modellbauer hinzu, der Ihre vom Kontext verwalteten Klassen überprüft und dann mithilfe einer Reihe von Regeln oder Konventionen bestimmt, wie diese Klassen und die Beziehungen ein Modell beschreiben und wie dieses Modell Ihrer Datenbank zugeordnet werden soll.
All dies geschieht zur Laufzeit. Sie werden dieses Modell nie sehen, es ist nur in Erinnerung.
Code First kann dieses Modell auch zum Erstellen einer Datenbank verwenden, wenn Sie dies möchten.
Es kann auch die Datenbank aktualisieren, wenn sich das Modell ändert, indem eine Funktion namens Code First Migrations verwendet wird.
Umgebungs-Setup
Um mit EF Code First arbeiten zu können, müssen die folgenden Tools auf Ihrem System installiert sein.
- Visual Studio 2013 (.net Framework 4.5.2) oder eine neuere Version.
- MS SQL Server 2012 oder höher.
- Entity Framework über NuGet Package.
Installieren Sie EF über das NuGet-Paket
Step 1 - Erstellen Sie zunächst die Konsolenanwendung über Datei → Neu → Projekt…
Step 2 - Wählen Sie im linken Bereich Windows und im Vorlagenbereich Konsolenanwendung aus.
Step 3 - Geben Sie als Namen EFCodeFirstDemo ein und wählen Sie OK.
Step 4 - Klicken Sie im Solution Explorer mit der rechten Maustaste auf Ihr Projekt und wählen Sie NuGet-Pakete verwalten…
Dadurch wird NuGet Package Manager geöffnet und nach EntityFramework gesucht. Dadurch wird nach allen Paketen gesucht, die sich auf Entity Framework beziehen.
Step 5- Wählen Sie EntityFramework und klicken Sie auf Installieren. Oder klicken Sie im Menü Extras auf NuGet Package Manager und dann auf Package Manager Console. Geben Sie im Fenster Package Manager Console den folgenden Befehl ein: Install-Package EntityFramework.
Nach Abschluss der Installation wird im Ausgabefenster die folgende Meldung angezeigt: "EntityFramework 6.1.2 'erfolgreich in EFCodeFirstDemo installiert".
Nach der Installation wird EntityFramework.dll in Ihr Projekt aufgenommen, wie in der folgenden Abbildung dargestellt.
Jetzt können Sie mit der Arbeit am Code First-Ansatz beginnen.
Definieren wir ein sehr einfaches Modell mithilfe von Klassen. Wir definieren sie nur in der Datei Program.cs, aber in einer realen Anwendung teilen Sie Ihre Klassen in separate Dateien und möglicherweise in ein separates Projekt auf. Im Folgenden finden Sie ein Datenmodell, das wir mit dem Code First-Ansatz erstellen werden.
Modell erstellen
Fügen Sie die folgenden drei Klassen in der Datei Program.cs hinzu, indem Sie den folgenden Code für die Schülerklasse verwenden.
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; }
}
Die ID-Eigenschaft wird zur Primärschlüsselspalte der Datenbanktabelle, die dieser Klasse entspricht.
Die Enrollments-Eigenschaft ist eine Navigationseigenschaft. Navigationseigenschaften enthalten andere Entitäten, die mit dieser Entität verknüpft sind.
In diesem Fall enthält die Enrollments-Eigenschaft einer Student-Entität alle Enrollment-Entitäten, die sich auf diese Student-Entität beziehen.
Navigationseigenschaften werden normalerweise als virtuell definiert, damit sie bestimmte Entity Framework-Funktionen wie z. B. verzögertes Laden nutzen können.
Wenn eine Navigationseigenschaft mehrere Entitäten enthalten kann (wie in Viele-zu-Viele- oder Eins-zu-Viele-Beziehungen), muss ihr Typ eine Liste sein, in der Einträge hinzugefügt, gelöscht und aktualisiert werden können, z. B. ICollection.
Es folgt die Implementierung für die Kursklasse.
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; }
}
Die Enrollments-Eigenschaft ist eine Navigationseigenschaft. Eine Kursentität kann mit einer beliebigen Anzahl von Registrierungsentitäten verknüpft werden.
Es folgt die Implementierung für die Registrierungsklasse und die Aufzählung.
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; }
}
Die EnrollmentID-Eigenschaft ist der Primärschlüssel.
Die Grade-Eigenschaft ist eine Aufzählung. Das Fragezeichen nach der Grade-Typ-Deklaration zeigt an, dass die Grade-Eigenschaft nullwertfähig ist.
Eine Note, die null ist, unterscheidet sich von einer Note null. Null bedeutet, dass eine Note nicht bekannt ist oder noch nicht vergeben wurde.
Die Eigenschaften StudentID und CourseID sind Fremdschlüssel, und die entsprechenden Navigationseigenschaften sind Student und Course.
Eine Registrierungsentität ist einer Schüler- und einer Kursentität zugeordnet, sodass die Eigenschaft nur eine einzelne Schüler- und Kursentität enthalten kann.
Datenbankkontext erstellen
Die Hauptklasse, die die Entity Framework-Funktionalität für ein bestimmtes Datenmodell koordiniert, ist die Datenbankkontextklasse, mit der Daten abgefragt und gespeichert werden können. Sie können diese Klasse erstellen, indem Sie sie von der DbContext-Klasse ableiten und ein typisiertes DbSet verfügbar machen
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; }
}
Im Folgenden finden Sie den vollständigen Code in der Datei Program.cs.
using System.ComponentModel.DataAnnotations.Schema;
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; }
}
}
Der obige Code ist alles, was wir zum Speichern und Abrufen von Daten benötigen. Fügen wir einige Daten hinzu und rufen sie dann ab. Es folgt der Code in der Hauptmethode.
static void Main(string[] args) {
using (var context = new MyContext()) {
// Create and save a new Students
Console.WriteLine("Adding new students");
var student = new Student {
FirstMidName = "Alain", LastName = "Bomer",
EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
};
context.Students.Add(student);
var student1 = new Student {
FirstMidName = "Mark", LastName = "Upston",
EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
};
context.Students.Add(student1);
context.SaveChanges();
// Display all Students from the database
var students = (from s in context.Students
orderby s.FirstMidName select s).ToList<Student>();
Console.WriteLine("Retrieve all Students from the database:");
foreach (var stdnt in students) {
string name = stdnt.FirstMidName + " " + stdnt.LastName;
Console.WriteLine("ID: {0}, Name: {1}", stdnt.ID, name);
}
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
Wenn der obige Code ausgeführt wird, erhalten Sie die folgende Ausgabe.
Adding new students
Retrieve all Students from the database:
ID: 1, Name: Alain Bomer
ID: 2, Name: Mark Upston
Press any key to exit...
Nun stellt sich die Frage, wo sich die Daten und die Datenbank befinden, in denen wir einige Daten hinzugefügt und dann aus der Datenbank abgerufen haben. Konventionell hat DbContext eine Datenbank für Sie erstellt.
Wenn eine lokale SQL Express-Instanz verfügbar ist, hat Code First die Datenbank auf dieser Instanz erstellt.
Wenn SQL Express nicht verfügbar ist, versucht Code First, LocalDb zu verwenden.
Die Datenbank ist nach dem vollständig qualifizierten Namen des abgeleiteten Kontexts benannt.
In unserem Fall ist eine SQL Express-Instanz verfügbar und der Datenbankname lautet EFCodeFirstDemo.MyContext (siehe folgende Abbildung).
Dies sind nur die Standardkonventionen, und es gibt verschiedene Möglichkeiten, die von Code First verwendete Datenbank zu ändern.
Wie Sie im obigen Bild sehen können, wurden Tabellen für Studenten, Kurse und Einschreibungen erstellt, und jede Tabelle enthält Spalten mit dem entsprechenden Datentyp und der entsprechenden Länge.
Die Spaltennamen und der Datentyp stimmen auch mit den Eigenschaften der jeweiligen Domänenklassen überein.
Datenbankinitialisierung
Im obigen Beispiel haben wir gesehen, dass Code First automatisch eine Datenbank erstellt. Wenn Sie jedoch den Namen der Datenbank und des Servers ändern möchten, lassen Sie uns sehen, wie Code First den Datenbanknamen und den Server beim Initialisieren einer Datenbank festlegt. Schauen Sie sich das folgende Diagramm an.
Sie können den Basiskonstruktor der Kontextklasse folgendermaßen definieren.
- Kein Parameter
- Name der Datenbank
- Name der Verbindungszeichenfolge
Kein Parameter
Wenn Sie den Basiskonstruktor der Kontextklasse ohne Parameter angeben, wie im obigen Beispiel gezeigt, erstellt das Entitätsframework eine Datenbank auf Ihrem lokalen SQLEXPRESS-Server mit dem Namen {Namespace}. {Name der Kontextklasse}.
Im obigen Beispiel hat die automatisch erstellte Datenbank den Namen EFCodeFirstDemo.MyContext. Wenn Sie sich den Namen ansehen, werden Sie feststellen, dass EFCodeFirstDemo der Namespace und MyContext der Name der Kontextklasse ist, wie im folgenden Code gezeigt.
public class MyContext : DbContext {
public MyContext() : base() {}
public virtual DbSet<Course> Courses { get; set; }
public virtual DbSet<Enrollment> Enrollments { get; set; }
public virtual DbSet<Student> Students { get; set; }
}
Name der Datenbank
Wenn Sie den Datenbanknamen als Parameter in einem Basiskonstruktor der Kontextklasse übergeben, erstellt Code First automatisch erneut eine Datenbank. Diesmal wird der Name jedoch als Parameter im Basiskonstruktor auf dem lokalen SQLEXPRESS-Datenbankserver übergeben .
Im folgenden Code wird MyContextDB als Parameter im Basiskonstruktor angegeben. Wenn Sie Ihre Anwendung ausführen, wird die Datenbank mit dem Namen MyContextDB auf Ihrem lokalen SQL Server erstellt.
public class MyContext : DbContext {
public MyContext() : base("MyContextDB") {}
public virtual DbSet<Course> Courses { get; set; }
public virtual DbSet<Enrollment> Enrollments { get; set; }
public virtual DbSet<Student> Students { get; set; }
}
Name der Verbindungszeichenfolge
Dies ist eine einfache Möglichkeit, DbContext anzuweisen, einen anderen Datenbankserver als SQL Express oder LocalDb zu verwenden. Sie können eine Verbindungszeichenfolge in Ihre Datei app.config einfügen.
Wenn der Name der Verbindungszeichenfolge mit dem Namen Ihres Kontexts übereinstimmt (entweder mit oder ohne Namespace-Qualifikation), wird er von DbContext gefunden, wenn der Konstruktor ohne Parameter verwendet wird.
Wenn sich der Name der Verbindungszeichenfolge vom Namen Ihres Kontexts unterscheidet, können Sie DbContext anweisen, diese Verbindung im Code First-Modus zu verwenden, indem Sie den Namen der Verbindungszeichenfolge an den DbContext-Konstruktor übergeben.
public class MyContext : DbContext {
public MyContext() : base("name = MyContextDB") {}
public virtual DbSet<Course> Courses { get; set; }
public virtual DbSet<Enrollment> Enrollments { get; set; }
public virtual DbSet<Student> Students { get; set; }
}
Im obigen Code wird im Basiskonstruktor ein Ausschnitt der Verbindungszeichenfolge der Kontextklasse als Parameter angegeben.
Der Name der Verbindungszeichenfolge muss mit "name =" beginnen, andernfalls wird er als Datenbankname betrachtet.
Dieses Formular macht deutlich, dass Sie erwarten, dass die Verbindungszeichenfolge in Ihrer Konfigurationsdatei gefunden wird. Eine Ausnahme wird ausgelöst, wenn keine Verbindungszeichenfolge mit dem angegebenen Namen gefunden wird.
<connectionStrings>
<add name = "MyContextDB"
connectionString = "Data Source =.;Initial Catalog = EFMyContextDB;Integrated Security = true"
providerName = "System.Data.SqlClient"/>
</connectionStrings>
Der Datenbankname in der Verbindungszeichenfolge in app.config lautet EFMyContextDB. CodeFirst erstellt eine neueEFMyContextDB Datenbank oder vorhandene verwenden EFMyContextDB Datenbank am lokalen SQL Server.
Domänenklassen
Bisher haben wir EF das Modell nur anhand seiner Standardkonventionen ermitteln lassen, aber es wird Zeiten geben, in denen unsere Klassen nicht den Konventionen entsprechen und wir in der Lage sein müssen, weitere Konfigurationen durchzuführen. Sie können diese Konventionen jedoch überschreiben, indem Sie Ihre Domänenklassen so konfigurieren, dass EF die erforderlichen Informationen erhält. Es gibt zwei Möglichkeiten, um Ihre Domänenklassen zu konfigurieren:
- Datenanmerkungen
- Fließende API
Datenanmerkungen
DataAnnotations wird zum Konfigurieren Ihrer Klassen verwendet, um die am häufigsten benötigten Konfigurationen hervorzuheben. DataAnnotations werden auch von einer Reihe von .NET-Anwendungen wie ASP.NET MVC verstanden, mit denen diese Anwendungen dieselben Annotationen für clientseitige Validierungen nutzen können.
Im Folgenden sind die Datenanmerkungen aufgeführt, die in der Schülerklasse verwendet werden.
public class Enrollment {
[Key]
public int EnrollmentID { get; set; }
public int CourseID { get; set; }
public int StudentID { get; set; }
public Grade? Grade { get; set; }
[ForeignKey("CourseID")]
public virtual Course Course { get; set; }
[ForeignKey("ID")]
public virtual Student Student { get; set; }
}
Fließende API
Die meisten Modellkonfigurationen können mit einfachen Datenanmerkungen durchgeführt werden. Die fließende API ist eine erweiterte Methode zur Angabe der Modellkonfiguration, die alle Funktionen von Datenanmerkungen abdeckt, zusätzlich zu einer erweiterten Konfiguration, die mit Datenanmerkungen nicht möglich ist. Datenanmerkungen und die fließende API können zusammen verwendet werden.
Um auf die fließende API zuzugreifen, überschreiben Sie die OnModelCreating-Methode in DbContext. Benennen Sie nun den Spaltennamen in der Schülertabelle von FirstMidName in FirstName um, 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; }
}
DataAnnotations wird verwendet, um die Klassen zu konfigurieren, die die am häufigsten benötigten Konfigurationen hervorheben. DataAnnotations werden auch von einer Reihe von .NET-Anwendungen verstanden, z. B. ASP.NET MVC, mit denen diese Anwendungen dieselben Annotationen für clientseitige Validierungen nutzen können. DataAnnotation-Attribute überschreiben die Standard-CodeFirst-Konventionen.
System.ComponentModel.DataAnnotations Enthält die folgenden Attribute, die sich auf die Nullfähigkeit oder Größe der Spalte auswirken.
- Key
- Timestamp
- ConcurrencyCheck
- Required
- MinLength
- MaxLength
- StringLength
System.ComponentModel.DataAnnotations.Schema Der Namespace enthält die folgenden Attribute, die sich auf das Schema der Datenbank auswirken.
- Table
- Column
- Index
- ForeignKey
- NotMapped
- InverseProperty
Schlüssel
Entity Framework basiert darauf, dass jede Entität einen Schlüsselwert hat, den sie zum Verfolgen von Entitäten verwendet. Eine der Konventionen, von denen Code First abhängt, ist, wie impliziert wird, welche Eigenschaft der Schlüssel in jeder der Code First-Klassen ist.
Konvention besteht darin, nach einer Eigenschaft mit dem Namen "Id" zu suchen oder nach einer Eigenschaft, die den Klassennamen und "Id" kombiniert, z. B. "StudentId".
Die Eigenschaft wird einer Primärschlüsselspalte in der Datenbank zugeordnet.
Die Klassen für Studenten, Kurse und Einschreibungen folgen dieser Konvention.
Nehmen wir nun an, die Schülerklasse hat den Namen StdntID anstelle von ID verwendet. Wenn Code First keine Eigenschaft findet, die dieser Konvention entspricht, wird eine Ausnahme ausgelöst, da Entity Framework erfordert, dass Sie über eine Schlüsseleigenschaft verfügen müssen. Mit der Schlüsselanmerkung können Sie angeben, welche Eigenschaft als EntityKey verwendet werden soll.
Schauen wir uns den folgenden Code einer Student-Klasse an, die StdntID enthält, aber nicht der Standardkonvention von Code First folgt. Um dies zu handhaben, wird ein Schlüsselattribut hinzugefügt, das es zu einem Primärschlüssel macht.
public class Student {
[Key]
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; }
}
Wenn Sie Ihre Anwendung ausführen und in SQL Server Explorer in Ihre Datenbank schauen, sehen Sie, dass der Primärschlüssel jetzt StdntID in der Schülertabelle ist.
Entity Framework unterstützt auch zusammengesetzte Schlüssel. Composite keyssind auch Primärschlüssel, die aus mehr als einer Eigenschaft bestehen. Sie haben beispielsweise eine DrivingLicense-Klasse, deren Primärschlüssel eine Kombination aus LicenseNumber und IssuingCountry ist.
public class DrivingLicense {
[Key, Column(Order = 1)]
public int LicenseNumber { get; set; }
[Key, Column(Order = 2)]
public string IssuingCountry { get; set; }
public DateTime Issued { get; set; }
public DateTime Expires { get; set; }
}
Wenn Sie über zusammengesetzte Schlüssel verfügen, müssen Sie in Entity Framework eine Reihenfolge der Schlüsseleigenschaften definieren. Sie können dies mithilfe der Spaltenanmerkung tun, um eine Reihenfolge anzugeben.
Zeitstempel
Code First behandelt Timestamp-Eigenschaften genauso wie ConcurrencyCheck-Eigenschaften, stellt jedoch auch sicher, dass das vom Code zuerst generierte Datenbankfeld nicht nullwertfähig ist.
Es ist üblicher, Zeilenversions- oder Zeitstempelfelder für die Parallelitätsprüfung zu verwenden.
Anstatt die ConcurrencyCheck-Annotation zu verwenden, können Sie die spezifischere TimeStamp-Annotation verwenden, solange der Typ der Eigenschaft ein Byte-Array ist.
Sie können nur eine Zeitstempeleigenschaft in einer bestimmten Klasse haben.
Schauen wir uns ein einfaches Beispiel an, indem wir der Course-Klasse die TimeStamp-Eigenschaft hinzufügen.
public class Course {
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
[Timestamp]
public byte[] TStamp { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
Wie Sie im obigen Beispiel sehen können, wird das Timestamp-Attribut auf die Byte [] -Eigenschaft der Course-Klasse angewendet. Code First erstellt also eine Zeitstempelspalte TStamp
in der Kurstabelle.
ConcurrencyCheck
Mit der ConcurrencyCheck-Annotation können Sie eine oder mehrere Eigenschaften markieren, die für die Parallelitätsprüfung in der Datenbank verwendet werden sollen, wenn ein Benutzer eine Entität bearbeitet oder löscht. Wenn Sie mit dem EF Designer gearbeitet haben, entspricht dies dem Festlegen des ConcurrencyMode einer Eigenschaft auf "Fest".
Schauen wir uns ein einfaches Beispiel für die Funktionsweise von ConcurrencyCheck an, indem wir es der Title-Eigenschaft in der Course-Klasse hinzufügen.
public class Course {
public int CourseID { get; set; }
[ConcurrencyCheck]
public string Title { get; set; }
public int Credits { get; set; }
[Timestamp, DataType("timestamp")]
public byte[] TimeStamp { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
In der obigen Kursklasse wird das ConcurrencyCheck-Attribut auf die vorhandene Title-Eigenschaft angewendet. Jetzt enthält Code First die Spalte "Titel" im Befehl "Aktualisieren", um die optimistische Parallelität zu überprüfen, wie im folgenden Code gezeigt.
exec sp_executesql N'UPDATE [dbo].[Courses]
SET [Title] = @0
WHERE (([CourseID] = @1) AND ([Title] = @2))
',N'@0 nvarchar(max) ,@1 int,@2 nvarchar(max) ',@0=N'Maths',@1=1,@2=N'Calculus'
go
Erforderliche Anmerkung
Die Annotation Erforderlich teilt EF mit, dass eine bestimmte Eigenschaft erforderlich ist. Werfen wir einen Blick auf die folgende Student-Klasse, in der der FirstMidName-Eigenschaft die erforderliche ID hinzugefügt wird. Das erforderliche Attribut zwingt EF, sicherzustellen, dass die Eigenschaft Daten enthält.
public class Student {
[Key]
public int StdntID { get; set; }
[Required]
public string LastName { get; set; }
[Required]
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
Wie im obigen Beispiel gezeigt, wird das Attribut Erforderlich auf Vorname und Nachname angewendet. Code First erstellt also in der Tabelle "Students" die Spalten NOT NULL FirstMidName und LastName, wie in der folgenden Abbildung dargestellt.
Maximale Länge
Mit dem MaxLength-Attribut können Sie zusätzliche Eigenschaftsüberprüfungen angeben. Es kann auf eine String- oder Array-Typ-Eigenschaft einer Domänenklasse angewendet werden. EF Code First legt die Größe einer Spalte fest, wie im MaxLength-Attribut angegeben.
Werfen wir einen Blick auf die folgende Kursklasse, in der das Attribut MaxLength (24) auf die Title-Eigenschaft angewendet wird.
public class Course {
public int CourseID { get; set; }
[ConcurrencyCheck]
[MaxLength(24)]
public string Title { get; set; }
public int Credits { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
Wenn Sie die obige Anwendung ausführen, erstellt Code First einen Titel in der Spalte nvarchar (24) in der CourseId-Tabelle, wie in der folgenden Abbildung dargestellt.
Wenn der Benutzer den Titel festlegt, der mehr als 24 Zeichen enthält, löst EF EntityValidationError aus.
Minimale Länge
Mit dem MinLength-Attribut können Sie ebenso wie mit MaxLength zusätzliche Eigenschaftsüberprüfungen angeben. Das MinLength-Attribut kann auch mit dem MaxLength-Attribut verwendet werden, wie im folgenden Code gezeigt.
public class Course {
public int CourseID { get; set; }
[ConcurrencyCheck]
[MaxLength(24) , MinLength(5)]
public string Title { get; set; }
public int Credits { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
EF löst EntityValidationError aus, wenn Sie einen Wert für die Title-Eigenschaft festlegen, der kleiner als die angegebene Länge im MinLength-Attribut oder größer als die angegebene Länge im MaxLength-Attribut ist.
String-Länge
Mit StringLength können Sie auch zusätzliche Eigenschaftsüberprüfungen wie MaxLength angeben. Der einzige Unterschied besteht darin, dass das StringLength-Attribut nur auf eine Zeichenfolgentyp-Eigenschaft von Domänenklassen angewendet werden kann.
public class Course {
public int CourseID { get; set; }
[StringLength (24)]
public string Title { get; set; }
public int Credits { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
Entity Framework überprüft auch den Wert einer Eigenschaft für das StringLength-Attribut. Wenn der Benutzer den Titel festlegt, der mehr als 24 Zeichen enthält, löst EF EntityValidationError aus.
Tabelle
Standardcode Die erste Konvention erstellt einen Tabellennamen ähnlich dem Klassennamen. Wenn Sie Code First die Datenbank erstellen lassen und auch den Namen der Tabellen ändern möchten, die erstellt werden. Dann -
Sie können Code First mit einer vorhandenen Datenbank verwenden. Es ist jedoch nicht immer so, dass die Namen der Klassen mit den Namen der Tabellen in Ihrer Datenbank übereinstimmen.
Das Tabellenattribut überschreibt diese Standardkonvention.
EF Code First erstellt eine Tabelle mit einem angegebenen Namen im Tabellenattribut für eine bestimmte Domänenklasse.
Schauen wir uns das folgende Beispiel an, in dem die Klasse Student heißt, und Code First geht davon aus, dass dies einer Tabelle mit dem Namen Students zugeordnet wird. Ist dies nicht der Fall, können Sie den Namen der Tabelle mit dem Tabellenattribut angeben, wie im folgenden Code gezeigt.
[Table("StudentsInfo")]
public class Student {
[Key]
public int StdntID { get; set; }
[Required]
public string LastName { get; set; }
[Required]
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
Jetzt können Sie sehen, dass das Tabellenattribut die Tabelle als StudentsInfo angibt. Wenn die Tabelle generiert wird, wird der Tabellenname StudentsInfo angezeigt (siehe folgende Abbildung).
Sie können nicht nur den Tabellennamen angeben, sondern auch ein Schema für die Tabelle mithilfe des Tabellenattributs angeben, wie im folgenden Code gezeigt.
[Table("StudentsInfo", Schema = "Admin")]
public class Student {
[Key]
public int StdntID { get; set; }
[Required]
public string LastName { get; set; }
[Required]
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
Im obigen Beispiel sehen Sie, dass die Tabelle mit dem Administratorschema angegeben ist. Jetzt erstellt Code First die StudentsInfo-Tabelle im Admin-Schema, wie in der folgenden Abbildung dargestellt.
Säule
Es ist auch dasselbe wie das Tabellenattribut, aber das Tabellenattribut überschreibt das Tabellenverhalten, während das Spaltenattribut das Spaltenverhalten überschreibt. Standardcode Die erste Konvention erstellt einen Spaltennamen ähnlich dem Eigenschaftsnamen. Wenn Sie Code First die Datenbank erstellen lassen und auch den Namen der Spalten in Ihren Tabellen ändern möchten. Dann -
Das Spaltenattribut überschreibt die Standardkonvention.
EF Code First erstellt eine Spalte mit einem angegebenen Namen im Column-Attribut für eine bestimmte Eigenschaft.
Schauen wir uns das folgende Beispiel an, in dem die Eigenschaft FirstMidName heißt. Gemäß der Konvention geht Code First davon aus, dass dies einer Spalte mit dem Namen FirstMidName zugeordnet wird.
Ist dies nicht der Fall, können Sie den Namen der Spalte mit dem Attribut Column angeben, wie im folgenden Code gezeigt.
public class Student {
public int ID { get; set; }
public string LastName { get; set; }
[Column("FirstName")]
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
Sie können sehen, dass das Spaltenattribut die Spalte als Vorname angibt. Wenn die Tabelle generiert wird, wird der Spaltenname FirstName angezeigt (siehe folgende Abbildung).
Index
Das Index-Attribut wurde in Entity Framework 6.1 eingeführt. Wenn Sie eine frühere Version verwenden, gelten die Informationen in diesem Abschnitt nicht.
Mit dem IndexAttribute können Sie einen Index für eine oder mehrere Spalten erstellen.
Durch Hinzufügen des Attributs zu einer oder mehreren Eigenschaften erstellt EF beim Erstellen der Datenbank den entsprechenden Index in der Datenbank.
Indizes machen das Abrufen von Daten in den meisten Fällen schneller und effizienter. Das Überladen einer Tabelle oder Ansicht mit Indizes kann jedoch die Leistung anderer Vorgänge wie Einfügungen oder Aktualisierungen unangenehm beeinträchtigen.
Die Indizierung ist die neue Funktion in Entity Framework, mit der Sie die Leistung Ihrer Code First-Anwendung verbessern können, indem Sie die Zeit reduzieren, die zum Abfragen von Daten aus der Datenbank erforderlich ist.
Sie können Ihrer Datenbank mithilfe des Indexattributs Indizes hinzufügen und die Standardeinstellungen für Eindeutig und Clustered überschreiben, um den für Ihr Szenario am besten geeigneten Index zu erhalten.
Standardmäßig heißt der Index IX_ <Eigenschaftsname>
Schauen wir uns den folgenden Code an, in dem das Indexattribut in der Kursklasse für Credits hinzugefügt wird.
public class Course {
public int CourseID { get; set; }
public string Title { get; set; }
[Index]
public int Credits { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
Sie können sehen, dass das Indexattribut auf die Credits-Eigenschaft angewendet wird. Wenn die Tabelle generiert wird, wird IX_Credits in den Indizes angezeigt.
Standardmäßig sind Indizes nicht eindeutig, Sie können jedoch die verwenden IsUniquebenannter Parameter, um anzugeben, dass ein Index eindeutig sein soll. Im folgenden Beispiel wird ein eindeutiger Index eingeführt, wie im folgenden Code gezeigt.
public class Course {
public int CourseID { get; set; }
[Index(IsUnique = true)]
public string Title { get; set; }
[Index]
public int Credits { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
Unbekannter Schlüssel
Die Code First-Konvention kümmert sich um die häufigsten Beziehungen in Ihrem Modell, aber es gibt einige Fälle, in denen Hilfe benötigt wird. Durch Ändern des Namens der Schlüsseleigenschaft in der Student-Klasse wurde beispielsweise ein Problem mit der Beziehung zur Enrollment-Klasse verursacht.
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 {
[Key]
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; }
}
Beim Generieren der Datenbank erkennt Code First die StudentID-Eigenschaft in der Enrollment-Klasse und erkennt sie gemäß der Konvention, dass sie mit einem Klassennamen plus „ID“ übereinstimmt, als Fremdschlüssel für die Student-Klasse. Es gibt jedoch keine StudentID-Eigenschaft in der Student-Klasse, aber die StdntID-Eigenschaft ist die Student-Klasse.
Die Lösung hierfür besteht darin, eine Navigationseigenschaft in der Registrierung zu erstellen und die ForeignKey DataAnnotation zu verwenden, um Code zu helfen. Verstehen Sie zunächst, wie die Beziehung zwischen den beiden Klassen aufgebaut wird, wie im folgenden Code gezeigt.
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; }
[ForeignKey("StudentID")]
public virtual Student Student { get; set; }
}
Sie können jetzt sehen, dass das ForeignKey-Attribut auf die Navigationseigenschaft angewendet wird.
NotMapped
Standardmäßig werden in der Datenbank alle Eigenschaften von Code First dargestellt, die von einem unterstützten Datentyp sind und Getter und Setter enthalten. Dies ist jedoch in Ihren Anwendungen nicht immer der Fall. Das NotMapped-Attribut überschreibt diese Standardkonvention. Beispielsweise haben Sie möglicherweise eine Eigenschaft in der Student-Klasse, z. B. FatherName, die jedoch nicht gespeichert werden muss. Sie können das NotMapped-Attribut auf eine FatherName-Eigenschaft anwenden, für die Sie keine Spalte in der Datenbank erstellen möchten, wie im folgenden Code gezeigt.
public class Student {
[Key]
public int StdntID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
[NotMapped]
public int FatherName { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
Sie können sehen, dass das NotMapped-Attribut auf die FatherName-Eigenschaft angewendet wird. Wenn die Tabelle generiert wird, sehen Sie, dass die FatherName-Spalte nicht in einer Datenbank erstellt wird, sondern in der Student-Klasse vorhanden ist.
Code First erstellt keine Spalte für eine Eigenschaft, die weder Getter noch Setter enthält, wie im folgenden Beispiel für die Eigenschaften Adresse und Alter der Schülerklasse gezeigt.
InverseProperty
InverseProperty wird verwendet, wenn Sie mehrere Beziehungen zwischen Klassen haben. In der Einschreibeklasse möchten Sie möglicherweise nachverfolgen, wer einen aktuellen und einen vorherigen Kurs eingeschrieben hat. Fügen wir zwei Navigationseigenschaften für die Registrierungsklasse hinzu.
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 CurrCourse { get; set; }
public virtual Course PrevCourse { get; set; }
public virtual Student Student { get; set; }
}
In ähnlicher Weise müssen Sie auch die Kursklasse hinzufügen, auf die diese Eigenschaften verweisen. Die Kursklasse verfügt über Navigationseigenschaften zurück zur Registrierungsklasse, die alle aktuellen und vorherigen Anmeldungen enthält.
public class Course {
public int CourseID { get; set; }
public string Title { get; set; }
[Index]
public int Credits { get; set; }
public virtual ICollection<Enrollment> CurrEnrollments { get; set; }
public virtual ICollection<Enrollment> PrevEnrollments { get; set; }
}
Code Erstellt zuerst die Fremdschlüsselspalte {Klassenname} _ {Primärschlüssel}, wenn die Fremdschlüsseleigenschaft nicht in einer bestimmten Klasse enthalten ist, wie in den obigen Klassen gezeigt. Wenn die Datenbank generiert wird, werden die folgenden Fremdschlüssel angezeigt.
Wie Sie sehen, kann Code first die Eigenschaften in den beiden Klassen nicht alleine abgleichen. Die Datenbanktabelle für Registrierungen sollte einen Fremdschlüssel für den CurrCourse und einen für den PrevCourse enthalten, aber Code First erstellt vier Fremdschlüsseleigenschaften, d. H.
- CurrCourse _CourseID
- PrevCourse _CourseID
- Course_CourseID und
- Course_CourseID1
Um diese Probleme zu beheben, können Sie die Annotation InverseProperty verwenden, um die Ausrichtung der Eigenschaften festzulegen.
public class Course {
public int CourseID { get; set; }
public string Title { get; set; }
[Index]
public int Credits { get; set; }
[InverseProperty("CurrCourse")]
public virtual ICollection<Enrollment> CurrEnrollments { get; set; }
[InverseProperty("PrevCourse")]
public virtual ICollection<Enrollment> PrevEnrollments { get; set; }
}
Wie Sie sehen können, wird das InverseProperty-Attribut in der obigen Kursklasse angewendet, indem angegeben wird, zu welcher Referenzeigenschaft der Registrierungsklasse sie gehört. Jetzt generiert Code First eine Datenbank und erstellt nur zwei Fremdschlüsselspalten in der Registrierungstabelle, wie in der folgenden Abbildung dargestellt.
Wir empfehlen, dass Sie das obige Beispiel zum besseren Verständnis Schritt für Schritt ausführen.
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 Schüler-, Kurs- und Einschreibungsklassen 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 und hier werden wir sehen, wie Sie mit 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; }
}
Kartenentitä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 möchten, 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 in 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 dem obigen Diagramm 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. Der Fremdschlüssel in der Einschreibungstabelle entspricht jedoch nicht den Standardkonventionen für 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.
In Entity Framework wurde Seed in EF 4.1 eingeführt und arbeitet mit Datenbankinitialisierern. Die allgemeine Idee einesSeed Methodbesteht darin, Daten in einer Datenbank zu initialisieren, die von Code First erstellt oder von Migrations entwickelt wird. Diese Daten sind häufig Testdaten, können aber auch Referenzdaten wie Listen bekannter Studenten, Kurse usw. sein. Wenn die Daten initialisiert werden, werden folgende Schritte ausgeführt:
- Überprüft, ob die Zieldatenbank bereits vorhanden ist.
- Wenn dies der Fall ist, wird das aktuelle Code First-Modell mit dem in Metadaten in der Datenbank gespeicherten Modell verglichen.
- Die Datenbank wird gelöscht, wenn das aktuelle Modell nicht mit dem Modell in der Datenbank übereinstimmt.
- Die Datenbank wird erstellt, wenn sie gelöscht wurde oder überhaupt nicht vorhanden war.
- Wenn die Datenbank erstellt wurde, wird die Initializer Seed-Methode aufgerufen.
Die Seed-Methode verwendet das Datenbankkontextobjekt als Eingabeparameter, und der Code in der Methode verwendet dieses Objekt, um der Datenbank neue Entitäten hinzuzufügen. Um Daten in Ihre Datenbank einzufügen, müssen Sie die Seed-Methode überschreiben. Schauen wir uns das folgende Beispiel an, in dem einige der Standarddaten in einer internen Klasse in die Datenbank initiiert werden.
private class UniDBInitializer<T> : DropCreateDatabaseAlways<MyContext> {
protected override void Seed(MyContext context) {
IList<Student> students = new List<Student>();
students.Add(new Student() {
FirstMidName = "Andrew",
LastName = "Peters",
EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
});
students.Add(new Student() {
FirstMidName = "Brice",
LastName = "Lambson",
EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
});
students.Add(new Student() {
FirstMidName = "Rowan",
LastName = "Miller",
EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
});
foreach (Student student in students)
context.Students.Add(student);
base.Seed(context);
}
}
Im obigen Code wird die Schülertabelle initialisiert. Sie müssen diese DB-Initialisierungsklasse in der Kontextklasse festlegen, wie im folgenden Code gezeigt.
public MyContext() : base("name=MyContextDB") {
Database.SetInitializer<MyContext>(new UniDBInitializer<MyContext>());
}
Im Folgenden finden Sie die vollständige Klassenimplementierung der MyContext-Klasse, die auch die DB-Initialisierungsklasse enthält.
public class MyContext : DbContext {
public MyContext() : base("name=MyContextDB") {
Database.SetInitializer<MyContext>(new UniDBInitializer<MyContext>());
}
public virtual DbSet<Course> Courses { get; set; }
public virtual DbSet<Enrollment> Enrollments { get; set; }
public virtual DbSet<Student> Students { get; set; }
private class UniDBInitializer<T> : DropCreateDatabaseAlways<MyContext> {
protected override void Seed(MyContext context) {
IList<Student> students = new List<Student>();
students.Add(new Student() {
FirstMidName = "Andrew",
LastName = "Peters",
EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
});
students.Add(new Student() {
FirstMidName = "Brice",
LastName = "Lambson",
EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
});
students.Add(new Student() {
FirstMidName = "Rowan",
LastName = "Miller",
EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
});
foreach (Student student in students)
context.Students.Add(student);
base.Seed(context);
}
}
}
Wenn das obige Beispiel kompiliert und ausgeführt wird, können Sie die Daten in einer Datenbank sehen, wie in der folgenden Abbildung gezeigt.
Wir empfehlen, dass Sie das obige Beispiel zum besseren Verständnis Schritt für Schritt ausführen.
Entity Framework 4.3 enthält eine neue Funktion für Code First Migrations, mit der Sie das Datenbankschema schrittweise weiterentwickeln können, wenn sich Ihr Modell im Laufe der Zeit ändert. Für die meisten Entwickler ist dies eine große Verbesserung gegenüber den Datenbankinitialisierungsoptionen aus den Versionen 4.1 und 4.2, bei denen Sie die Datenbank manuell aktualisieren oder löschen und neu erstellen mussten, wenn sich Ihr Modell änderte.
Wenn Sie vor Entity Framework 4.3 bereits Daten (außer Startdaten) oder vorhandene gespeicherte Prozeduren, Trigger usw. in Ihrer Datenbank haben, werden diese Strategien verwendet, um die gesamte Datenbank zu löschen und neu zu erstellen, sodass Sie die Daten und andere Datenbanken verlieren Objekte.
Bei der Migration wird das Datenbankschema automatisch aktualisiert, wenn sich Ihr Modell ändert, ohne dass vorhandene Daten oder andere Datenbankobjekte verloren gehen.
Es wird ein neuer Datenbankinitialisierer namens MigrateDatabaseToLatestVersion verwendet.
Es gibt zwei Arten der Migration -
- Automatisierte Migration
- Codebasierte Migration
Automatisierte Migration
Die automatisierte Migration wurde erstmals in Entity Framework 4.3 eingeführt. Bei der automatisierten Migration müssen Sie die Datenbankmigration nicht manuell in der Codedatei verarbeiten. Beispielsweise müssen Sie für jede Änderung auch Ihre Domänenklassen ändern. Bei der automatisierten Migration müssen Sie jedoch nur einen Befehl in der Package Manager-Konsole ausführen, um dies zu erledigen.
Schauen wir uns den folgenden schrittweisen Prozess der automatisierten Migration an.
Wenn Sie den Code First-Ansatz verwenden, haben Sie keine Datenbank für Ihre Anwendung.
In diesem Beispiel beginnen wir mit unseren 3 Grundklassen wie Student, Kurs und Einschreibung, wie im folgenden Code gezeigt.
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; }
[Index]
public int Credits { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
Es folgt die Kontextklasse.
public class MyContext : DbContext {
public MyContext() : base("MyContextDB") {}
public virtual DbSet<Course> Courses { get; set; }
public virtual DbSet<Enrollment> Enrollments { get; set; }
public virtual DbSet<Student> Students { get; set; }
}
Bevor Sie die Anwendung ausführen, müssen Sie die automatische Migration aktivieren.
Step 1 - Öffnen Sie die Package Manager-Konsole über Tools → NuGet Package Manager → Package Manager-Konsole.
Step 2 - Um die automatische Migration zu aktivieren, führen Sie den folgenden Befehl in der Package Manager-Konsole aus.
PM> enable-migrations -EnableAutomaticMigrations:$true
Step 3 - Sobald der Befehl erfolgreich ausgeführt wurde, wird eine interne versiegelte Konfigurationsklasse im Migrationsordner Ihres Projekts erstellt, wie im folgenden Code gezeigt.
namespace EFCodeFirstDemo.Migrations {
using System;
using System.Data.Entity;
using System.Data.Entity.Migrations;
using System.Linq;
internal sealed class Configuration : DbMigrationsConfiguration<EFCodeFirstDemo.MyContext> {
public Configuration() {
AutomaticMigrationsEnabled = true;
ContextKey = "EFCodeFirstDemo.MyContext";
}
protected override void Seed(EFCodeFirstDemo.MyContext context) {
// This method will be called after migrating to the latest version.
// You can use the DbSet<T>.AddOrUpdate() helper extension method
// to avoid creating duplicate seed data. E.g.
// context.People.AddOrUpdate(
// p ⇒ p.FullName,
// new Person { FullName = "Andrew Peters" },
// new Person { FullName = "Brice Lambson" },
// new Person { FullName = "Rowan Miller" }
// );
}
}
}
Step 4 - Legen Sie den Datenbankinitialisierer in der Kontextklasse mit der neuen DB-Initialisierungsstrategie MigrateDatabaseToLatestVersion fest.
public class MyContext : DbContext {
public MyContext() : base("MyContextDB") {
Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyContext,
EFCodeFirstDemo.Migrations.Configuration>("MyContextDB"));
}
public virtual DbSet<Course> Courses { get; set; }
public virtual DbSet<Enrollment> Enrollments { get; set; }
public virtual DbSet<Student> Students { get; set; }
}
Step 5- Sie haben die automatische Migration eingerichtet. Wenn Sie Ihre Anwendung ausführen, übernimmt sie automatisch die Migration, wenn Sie das Modell ändern.
Step 6- Wie Sie sehen, wird eine Systemtabelle __MigrationHistory auch mit anderen Tabellen in Ihrer Datenbank erstellt. In __MigrationHistory verwaltet die automatisierte Migration den Verlauf von Datenbankänderungen.
Step 7- Wenn Sie eine weitere Entitätsklasse als Domänenklasse hinzufügen und Ihre Anwendung ausführen, wird die Tabelle in Ihrer Datenbank erstellt. Fügen wir die folgende StudentLogIn-Klasse hinzu.
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; }
}
Step 8 - Vergessen Sie nicht, das DBSet für die oben genannte Klasse in Ihre Kontextklasse einzufügen, wie im folgenden Code gezeigt.
public virtual DbSet<StudentLogIn> StudentsLogIn { get; set; }
Step 9 - Führen Sie Ihre Anwendung erneut aus, und Sie werden sehen, dass die StudentsLogIn-Tabelle zu Ihrer Datenbank hinzugefügt wurde.
Die oben genannten Schritte für automatisierte Migrationen funktionieren nur für Ihre Entität. Wenn Sie beispielsweise eine andere Entitätsklasse hinzufügen oder die vorhandene Entitätsklasse entfernen möchten, wird die Migration erfolgreich durchgeführt. Wenn Sie Ihrer Entitätsklasse jedoch eine Eigenschaft hinzufügen oder entfernen, wird eine Ausnahme ausgelöst.
Step 10 - Um die Eigenschaftsmigration zu handhaben, müssen Sie AutomaticMigrationDataLossAllowed = true im Konfigurationsklassenkonstruktor festlegen.
public Configuration() {
AutomaticMigrationsEnabled = true;
AutomaticMigrationDataLossAllowed = true;
ContextKey = "EFCodeFirstDemo.MyContext";
}
Codebasierte Migration
Wenn Sie eine neue Anwendung entwickeln, ändert sich Ihr Datenmodell häufig, und jedes Mal, wenn sich das Modell ändert, wird es nicht mehr mit der Datenbank synchronisiert. Sie haben das Entity Framework so konfiguriert, dass die Datenbank bei jeder Änderung des Datenmodells automatisch gelöscht und neu erstellt wird. Die codebasierte Migration ist nützlich, wenn Sie mehr Kontrolle über die Migration wünschen.
Wenn Sie Entitätsklassen hinzufügen, entfernen oder ändern oder Ihre DbContext-Klasse ändern, wird beim nächsten Ausführen der Anwendung Ihre vorhandene Datenbank automatisch gelöscht, eine neue erstellt, die dem Modell entspricht, und mit Testdaten versehen.
Die Funktion "Code First Migrations" löst dieses Problem, indem Code First das Datenbankschema aktualisieren kann, anstatt die Datenbank zu löschen und neu zu erstellen. Um die Anwendung bereitzustellen, müssen Sie Migrationen aktivieren.
Hier ist die Grundregel zum Migrieren von Änderungen in der Datenbank:
- Migrationen aktivieren
- Migration hinzufügen
- Datenbank auf den neusten Stand bringen
Schauen wir uns den folgenden schrittweisen Prozess der Code-Base-Migration an.
Wenn Sie den Code First-Ansatz verwenden, haben Sie keine Datenbank für Ihre Anwendung.
In diesem Beispiel beginnen wir erneut mit unseren 3 Grundklassen wie Schüler, Kurs und Einschreibung, wie im folgenden Code gezeigt.
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; }
[Index]
public int Credits { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
Es folgt die Kontextklasse.
public class MyContext : DbContext {
public MyContext() : base("MyContextDB") {
Database.SetInitializer(new MigrateDatabaseToLatestVersion<
MyContext, EFCodeFirstDemo.Migrations.Configuration>("MyContextDB"));
}
public virtual DbSet<Course> Courses { get; set; }
public virtual DbSet<Enrollment> Enrollments { get; set; }
public virtual DbSet<Student> Students { get; set; }
}
Step 1 - Bevor Sie die Anwendung ausführen, müssen Sie die Migration aktivieren.
Step 2 - Öffnen Sie die Package Manager-Konsole über Tools → NuGet Package Manager → Package Manager-Konsole.
Step 3 - Die Migration ist bereits aktiviert. Fügen Sie nun die Migration in Ihrer Anwendung hinzu, indem Sie den folgenden Befehl ausführen.
PM> add-migration "UniDB Schema"
Step 4 - Wenn der Befehl erfolgreich ausgeführt wurde, wird im Ordner Migration eine neue Datei mit dem Namen des Parameters erstellt, den Sie mit einem Zeitstempelpräfix an den Befehl übergeben haben (siehe folgende Abbildung).
Step 5 - Sie können die Datenbank mit dem Befehl "update-database" erstellen oder aktualisieren.
PM> Update-Database -Verbose
Das Flag "-Verbose" gibt an, dass die SQL-Anweisungen angezeigt werden, die auf die Zieldatenbank in der Konsole angewendet werden.
Step 6 - Fügen wir der Schülerklasse eine weitere Eigenschaft 'Age' hinzu und führen dann die Update-Anweisung aus.
public class Student {
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public int Age { get; set; }
public DateTime EnrollmentDate { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
Wenn Sie PM → Update-Database –Verbose ausführen und der Befehl erfolgreich ausgeführt wird, wird die neue Spalte Age in Ihrer Datenbank hinzugefügt.
Wir empfehlen, dass Sie das obige Beispiel zum besseren Verständnis Schritt für Schritt ausführen.
In diesem Kapitel erfahren Sie, wie Sie Änderungen in die Datenbank migrieren, wenn die Anwendung mehrere DbContext-Klassen enthält.
- Multiple DbContext wurde erstmals in Entity Framework 6.0 eingeführt.
- Mehrere Kontextklassen können zu einer einzelnen Datenbank oder zu zwei verschiedenen Datenbanken gehören.
In unserem Beispiel definieren wir zwei Kontextklassen für dieselbe Datenbank. Im folgenden Code gibt es zwei DbContext-Klassen für Schüler und Lehrer.
public class Student {
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
}
public class MyStudentContext : DbContext {
public MyStudentContext() : base("UniContextDB") {}
public virtual DbSet<Student> Students { get; set; }
}
public class Teacher {
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime HireDate { get; set; }
}
public class MyTeacherContext : DbContext {
public MyTeacherContext() : base("UniContextDB") {}
public virtual DbSet<Teacher> Teachers { get; set; }
}
Wie Sie im obigen Code sehen können, gibt es zwei Modelle mit den Namen "Schüler" und "Lehrer". Jeder ist einer bestimmten entsprechenden Kontextklasse zugeordnet, dh Schüler ist MyStudentContext und Lehrer ist MyTeacherContext zugeordnet.
Hier ist die Grundregel zum Migrieren von Änderungen in der Datenbank, wenn mehrere Kontextklassen innerhalb desselben Projekts vorhanden sind.
enable-migrations -ContextTypeName <DbContext-Name-with-Namespaces> MigrationsDirectory: <Migrations-Directory-Name>
Add-Migration-Konfiguration <DbContext-Migrations-Configuration-Class-withNamespaces> <Migrations-Name>
Update-Datenbank-Konfiguration <DbContext-Migrations-Configuration-Class-withNamespaces> -Verbose
Aktivieren Sie die Migration für MyStudentContext, indem Sie den folgenden Befehl in der Package Manager-Konsole ausführen.
PM→ enable-migrations -ContextTypeName:EFCodeFirstDemo.MyStudentContext
Sobald es ausgeführt wird, fügen wir das Modell in den Migrationsverlauf ein und müssen dafür den Befehl add-migration in derselben Konsole auslösen.
PM→ add-migration -configuration EFCodeFirstDemo.Migrations.Configuration Initial
Fügen wir nun einige Daten zu den Schüler- und Lehrertabellen in der Datenbank hinzu.
static void Main(string[] args) {
using (var context = new MyStudentContext()) {
//// Create and save a new Students
Console.WriteLine("Adding new students");
var student = new Student {
FirstMidName = "Alain",
LastName = "Bomer",
EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
//Age = 24
};
context.Students.Add(student);
var student1 = new Student {
FirstMidName = "Mark",
LastName = "Upston",
EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
//Age = 30
};
context.Students.Add(student1);
context.SaveChanges();
// Display all Students from the database
var students = (from s in context.Students orderby s.FirstMidName
select s).ToList<Student>();
Console.WriteLine("Retrieve all Students from the database:");
foreach (var stdnt in students) {
string name = stdnt.FirstMidName + " " + stdnt.LastName;
Console.WriteLine("ID: {0}, Name: {1}", stdnt.ID, name);
}
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
using (var context = new MyTeacherContext()) {
//// Create and save a new Teachers
Console.WriteLine("Adding new teachers");
var student = new Teacher {
FirstMidName = "Alain",
LastName = "Bomer",
HireDate = DateTime.Parse(DateTime.Today.ToString())
//Age = 24
};
context.Teachers.Add(student);
var student1 = new Teacher {
FirstMidName = "Mark",
LastName = "Upston",
HireDate = DateTime.Parse(DateTime.Today.ToString())
//Age = 30
};
context.Teachers.Add(student1);
context.SaveChanges();
// Display all Teachers from the database
var teachers = (from t in context.Teachers orderby t.FirstMidName
select t).ToList<Teacher>();
Console.WriteLine("Retrieve all teachers from the database:");
foreach (var teacher in teachers) {
string name = teacher.FirstMidName + " " + teacher.LastName;
Console.WriteLine("ID: {0}, Name: {1}", teacher.ID, name);
}
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
Wenn der obige Code ausgeführt wird, werden Sie sehen, dass zwei verschiedene Tabellen für zwei verschiedene Modelle erstellt werden, wie in der folgenden Abbildung gezeigt.
Wir empfehlen, dass Sie das obige Beispiel zum besseren Verständnis Schritt für Schritt ausführen.
Vor Entity Framework 6 erkannte Entity Framework keine Entitäten oder komplexen Typen, die in anderen Entitäten oder komplexen Typen verschachtelt waren. Als Entity Framework das Modell generierte, verschwanden die verschachtelten Typen einfach.
Schauen wir uns ein einfaches Beispiel an, in dem wir unser Grundmodell mit drei Entitäten Student, Kurs und Einschreibung haben.
Fügen wir eine Eigenschaftsidentität hinzu, bei der es sich um einen Personentyp handelt. Person ist eine andere Entität, enthält die Eigenschaften BirthDate und FatherName.
In Entity Framework-Begriffen handelt es sich um einen komplexen Entity Framework-Typ, da es keine Identität hat und Teil einer Entität ist. Seit der ersten Version von Entity Framework werden komplexe Typen unterstützt.
Der Personentyp ist nicht wie im folgenden Code gezeigt verschachtelt.
public class Student {
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public Person Identity { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
public class Person {
public Person(string fatherName, DateTime birthDate) {
FatherName = fatherName;
BirthDate = birthDate;
}
public string FatherName { get; set; }
public DateTime BirthDate { get; set; }
}
Entity Framework weiß, wie Personentypen beibehalten werden, wenn es auch in früheren Versionen verwendet wird.
Mithilfe des Entity Framework Power Tools können wir sehen, wie Entity Framework das Modell interpretiert. Klicken Sie mit der rechten Maustaste auf die Datei Program.cs und wählen Sie Entity Framework → Entity Data Model anzeigen (schreibgeschützt).
Jetzt sehen Sie, dass die Identitätseigenschaft in der Schülerklasse definiert ist.
Wenn diese Person-Klasse von keiner anderen Entität verwendet wird, können wir sie in der Student-Klasse verschachteln, aber diese frühere Version von Entity Framework erkennt keine verschachtelten Typen an.
In einer älteren Version generieren Sie das Modell erneut. Der Typ wird nicht nur nicht erkannt, sondern auch, da er nicht vorhanden ist, ist die Eigenschaft auch nicht vorhanden, sodass Entity Framework den Personentyp überhaupt nicht beibehält.
public class Student {
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public Person Identity { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
public class Person {
public Person(string fatherName, DateTime birthDate) {
FatherName = fatherName;
BirthDate = birthDate;
}
public string FatherName { get; set; }
public DateTime BirthDate { get; set; }
}
}
Mit Entity Framework 6 werden verschachtelte Entitäten und komplexe Typen erkannt. Im obigen Code können Sie sehen, dass die Person in der Schülerklasse verschachtelt ist.
Wenn Sie das Entity Framework Power Tool verwenden, um zu zeigen, wie Entity Framework das Modell dieses Mal interpretiert, gibt es eine echte Identitätseigenschaft und einen komplexen Personentyp. Entity Framework behält diese Daten also bei.
Jetzt können Sie sehen, dass Identität ein verschachtelter Entitätstyp ist, der vor Entity Framework 6 nicht unterstützt wurde.
Wir empfehlen, dass Sie das obige Beispiel zum besseren Verständnis Schritt für Schritt ausführen.