Lernprogramm: Erstellen eines komplexeren Datenmodells für eine ASP.NET MVC-App
In den vorherigen Lernprogrammen haben Sie mit einem einfachen Datenmodell gearbeitet, das aus drei Entitäten besteht. In diesem Lernprogramm fügen Sie weitere Entitäten und Beziehungen hinzu und passen das Datenmodell an, indem Sie Formatierungs-, Validierungs- und Datenbankzuordnungsregeln angeben. In diesem Artikel werden zwei Möglichkeiten zum Anpassen des Datenmodells gezeigt: durch Hinzufügen von Attributen zu Entitätsklassen und Hinzufügen von Code zur Datenbankkontextklasse.
Wenn Sie dies erledigt haben, bilden die Entitätsklassen ein vollständiges Datenmodell, das in der folgenden Abbildung dargestellt wird:
In diesem Tutorial:
- Anpassen des Datenmodells
- Schülerentität aktualisieren
- Erstellen der Entität „Instructor“
- Erstellen der Entität „OfficeAssignment“
- Ändern der Kursentität
- Erstellen der Entität „Department“
- Ändern der Entität „Enrollment“
- Hinzufügen von Code zum Datenbankkontext
- Füllen der Datenbank mit Testdaten
- Hinzufügen einer Migration
- Aktualisieren der Datenbank
Voraussetzungen
Anpassen des Datenmodells
In diesem Abschnitt wird das Anpassen des Datenmodells mithilfe von Attributen erläutert, die Regeln zur Formatierung, Validierung und Datenbankzuordnung angeben. Anschließend erstellen Sie in mehreren der folgenden Abschnitte das vollständige School
Datenmodell, indem Sie den bereits erstellten Klassen Attribute hinzufügen und neue Klassen für die verbleibenden Entitätstypen im Modell erstellen.
Das DataType-Attribut
Für die Anmeldedaten von Studenten zeigen alle Webseiten derzeit die Zeit und das Datum an, obwohl für dieses Feld nur das Datum relevant ist. Indem Sie Attribute für die Datenanmerkung verwenden, können Sie eine Codeänderungen vornehmen, durch die das Anzeigeformat in jeder Ansicht korrigiert wird, in der die Daten angezeigt werden. Sie können dies beispielhaft testen, indem Sie ein Attribut zur EnrollmentDate
-Eigenschaft der Student
-Klasse hinzufügen.
Fügen Sie in Models\Student.cs eine using
Anweisung für den System.ComponentModel.DataAnnotations
Namespace hinzu, und fügen Sie der EnrollmentDate
Eigenschaft Attribute hinzu DataType
DisplayFormat
, wie im folgenden Beispiel gezeigt:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace ContosoUniversity.Models
{
public class Student
{
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime EnrollmentDate { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
}
Das DataType-Attribut wird verwendet, um einen spezifischeren Datentyp als den systeminternen Datenbanktyp anzugeben. In diesem Fall soll nur das Datum verfolgt werden, nicht das Datum und die Zeit. Die DataType-Aufzählung stellt viele Datentypen bereit, z . B. Datum, Uhrzeit, PhoneNumber, Währung, EmailAddress und mehr. Das DataType
-Attribut kann der Anwendung auch ermöglichen, typspezifische Features bereitzustellen. Beispielsweise kann ein mailto:
Link für DataType.EmailAddress erstellt werden, und eine Datumsauswahl kann für DataType.Date in Browsern bereitgestellt werden, die HTML5 unterstützen. Die DataType-Attribute geben HTML 5-Daten - (ausgeprägte Datenstrich)-Attribute aus, die HTML 5-Browser verstehen können. Die DataType-Attribute stellen keine Überprüfung bereit.
DataType.Date
gibt nicht das Format des Datums an, das angezeigt wird. Standardmäßig wird das Datenfeld entsprechend den Standardformaten basierend auf der CultureInfo des Servers angezeigt.
Das DisplayFormat
-Attribut dient zum expliziten Angeben des Datumsformats:
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
Die ApplyFormatInEditMode
Einstellung gibt an, dass die angegebene Formatierung auch angewendet werden soll, wenn der Wert in einem Textfeld zum Bearbeiten angezeigt wird. (Möglicherweise möchten Sie dies für einige Felder nicht, z. B. für Währungswerte, das Währungssymbol im Textfeld zur Bearbeitung nicht verwenden.)
Sie können das DisplayFormat-Attribut selbst verwenden, in der Regel ist es jedoch ratsam, auch das DataType-Attribut zu verwenden. Das DataType
Attribut vermittelt die Semantik der Daten im Gegensatz zum Rendern auf einem Bildschirm und bietet die folgenden Vorteile, die Sie nicht erhalten DisplayFormat
:
- Der Browser kann HTML5-Features aktivieren (z.B. zum Anzeigen eines Kalendersteuerelements, des dem Gebietsschema entsprechenden Währungssymbols, von E-Mail-Links, einigen clientseitigen Eingabevalidierungen usw.).
- Standardmäßig rendert der Browser Daten mithilfe des richtigen Formats basierend auf Ihrem Gebietsschema.
- Mit dem DataType-Attribut kann MVC die richtige Feldvorlage auswählen, um die Daten zu rendern (das DisplayFormat verwendet die Zeichenfolgenvorlage). Weitere Informationen finden Sie unter Brad Wilsons ASP.NET MVC 2 Templates. (Obwohl für MVC 2 geschrieben, gilt dieser Artikel weiterhin für die aktuelle Version von ASP.NET MVC.)
Wenn Sie das DataType
Attribut mit einem Datumsfeld verwenden, müssen Sie das DisplayFormat
Attribut auch angeben, um sicherzustellen, dass das Feld in Chrome-Browsern ordnungsgemäß gerendert wird. Weitere Informationen finden Sie in diesem StackOverflow-Thread.
Weitere Informationen zum Behandeln anderer Datumsformate in MVC finden Sie in der MVC 5-Einführung: Untersuchen der Bearbeitungsmethoden und Bearbeiten der Ansicht und Suche auf der Seite nach "Internationalisierung".
Führen Sie die Seite "Kursteilnehmerindex" erneut aus, und beachten Sie, dass zeiten für die Anmeldedaten nicht mehr angezeigt werden. Das gleiche gilt für jede Ansicht, die das Student
Modell verwendet.
The StringLengthAttribute
Sie können ebenfalls Regeln für die Datenvalidierung und Meldungen für Validierungsfehler mithilfe von Attributen angeben. Das StringLength-Attribut legt die maximale Länge in der Datenbank fest und stellt clientseitige und serverseitige Überprüfung für ASP.NET MVC bereit. Sie können ebenfalls die mindestens erforderliche Zeichenfolgenlänge in diesem Attribut angeben, dieser Wert hat jedoch keine Auswirkung auf das Datenbankschema.
Angenommen, Sie möchten sicherstellen, dass Benutzer nicht mehr als 50 Zeichen für einen Namen eingeben. Um diese Einschränkung hinzuzufügen, fügen Sie stringLength-Attribute zu den LastName
und FirstMidName
Eigenschaften hinzu, wie im folgenden Beispiel gezeigt:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace ContosoUniversity.Models
{
public class Student
{
public int ID { get; set; }
[StringLength(50)]
public string LastName { get; set; }
[StringLength(50, ErrorMessage = "First name cannot be longer than 50 characters.")]
public string FirstMidName { get; set; }
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime EnrollmentDate { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
}
Das StringLength-Attribut verhindert nicht, dass ein Benutzer Leerzeichen für einen Namen eingibt. Sie können das RegularExpression-Attribut verwenden, um Einschränkungen auf die Eingabe anzuwenden. Der folgende Code erfordert beispielsweise, dass das erste Zeichen Großbuchstaben und die verbleibenden Zeichen alphabetisch sein müssen:
[RegularExpression(@"^[A-Z]+[a-zA-Z""'\s-]*$")]
Das MaxLength-Attribut bietet ähnliche Funktionen wie das StringLength-Attribut , bietet aber keine clientseitige Überprüfung.
Führen Sie die Anwendung aus, und klicken Sie auf die Registerkarte "Kursteilnehmer ". Folgende Fehlermeldung wird angezeigt:
Das Modell, das den Kontext "SchoolContext" unterstützt, wurde seit der Erstellung der Datenbank geändert. Erwägen Sie die Verwendung von Code First-Migrationen zum Aktualisieren der Datenbank (https://go.microsoft.com/fwlink/?LinkId=238269).
Das Datenbankmodell hat sich auf eine Weise geändert, die eine Änderung des Datenbankschemas erfordert, und Entity Framework hat erkannt, dass dies erfolgt. Sie verwenden Migrationen, um das Schema zu aktualisieren, ohne daten zu verlieren, die Sie der Datenbank mithilfe der Benutzeroberfläche hinzugefügt haben. Wenn Sie Daten geändert haben, die von der Seed
Methode erstellt wurden, wird dies aufgrund der AddOrUpdate-Methode , die Sie in der Seed
Methode verwenden, wieder in den ursprünglichen Zustand geändert. (AddOrUpdate entspricht einem Upsert-Vorgang aus der Datenbankterminologie.)
Geben Sie die folgenden Befehle in die Paket-Manager-Konsole ein:
add-migration MaxLengthOnNames
update-database
Der add-migration
Befehl erstellt eine Datei mit dem Namen <"timeStamp>_MaxLengthOnNames.cs". Diese Datei enthält Code in der Up
-Methode, durch den die Datenbank dem aktuellen Datenmodell entsprechend aktualisiert wird. Der Code wurde durch den update-database
-Befehl ausgeführt.
Der zeitstempel, der dem Migrationsdateinamen vorangestellt ist, wird von Entity Framework verwendet, um die Migrationen zu ordnen. Sie können mehrere Migrationen erstellen, bevor Sie den update-database
Befehl ausführen, und dann werden alle Migrationen in der Reihenfolge angewendet, in der sie erstellt wurden.
Führen Sie die Seite "Erstellen" aus, und geben Sie einen der Namen ein, der länger als 50 Zeichen ist. Wenn Sie auf "Erstellen" klicken, zeigt die clientseitige Überprüfung eine Fehlermeldung an: Das Feld "Nachname" muss eine Zeichenfolge mit einer maximalen Länge von 50 sein.
Das Column-Attribut
Sie können ebenfalls Attribute verwenden, um zu steuern, wie Ihre Klassen und Eigenschaften der Datenbank zugeordnet werden. Angenommen, Sie haben den Namen FirstMidName
für das Feld „first-name“ verwendet, da das Feld ebenfalls einen Zweitnamen enthalten kann. Sie möchten jedoch, dass die Datenbankspalte in FirstName
umbenannt wird, damit Benutzer, die Ad-hob-Abfragen für die Datenbank schreiben, an diesen Namen gewöhnt sind. Sie können das Column
-Attribut verwenden, um diese Zuordnung durchzuführen.
Das Column
-Attribut gibt an, dass bei der Erstellung der Datenbank die Spalte des Student
-Elements, die der FirstMidName
-Eigenschaft zugeordnet ist, den Namen FirstName
erhält. Wenn Ihr Code also auf Student.FirstMidName
verweist, stammen die Daten aus der FirstName
-Spalte der Student
-Tabelle oder werden in dieser aktualisiert. Wenn Sie keine Spaltennamen angeben, erhalten sie denselben Namen wie der Eigenschaftsname.
Fügen Sie in der datei Student.cs eine using
Anweisung für System.ComponentModel.DataAnnotations.Schema hinzu, und fügen Sie der Eigenschaft das Spaltennamen-Attribut hinzu FirstMidName
, wie im folgenden hervorgehobenen Code gezeigt:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace ContosoUniversity.Models
{
public class Student
{
public int ID { get; set; }
[StringLength(50)]
public string LastName { get; set; }
[StringLength(50, ErrorMessage = "First name cannot be longer than 50 characters.")]
[Column("FirstName")]
public string FirstMidName { get; set; }
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime EnrollmentDate { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
}
Das Hinzufügen des Column-Attributs ändert das Modell, das schoolContext zurückgibt, sodass es nicht mit der Datenbank übereinstimmt. Geben Sie die folgenden Befehle im PMC ein, um eine weitere Migration zu erstellen:
add-migration ColumnFirstName
update-database
Öffnen Sie im Server-Explorer den Tabellen-Designer "Student", indem Sie auf die Tabelle "Student" doppelklicken.
Die folgende Abbildung zeigt den ursprünglichen Spaltennamen wie vor der Anwendung der ersten beiden Migrationen. Neben dem Spaltennamen, der von FirstMidName
zu FirstName
" geändert wird , haben sich die beiden Namensspalten von MAX
Länge zu 50 Zeichen geändert.
Sie können auch Änderungen an der Datenbankzuordnung mithilfe der Fluent-API vornehmen, wie Sie weiter unten in diesem Lernprogramm sehen.
Hinweis
Wenn Sie vor dem Erstellen aller Entitätsklassen in den folgenden Abschnitten versuchen, zu kompilieren, werden möglicherweise Compilerfehler angezeigt.
Schülerentität aktualisieren
Ersetzen Sie in "Models\Student.cs" den Code, den Sie zuvor hinzugefügt haben, durch den folgenden Code. Die Änderungen werden hervorgehoben.
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace ContosoUniversity.Models
{
public class Student
{
public int ID { get; set; }
[Required]
[StringLength(50)]
[Display(Name = "Last Name")]
public string LastName { get; set; }
[Required]
[StringLength(50, ErrorMessage = "First name cannot be longer than 50 characters.")]
[Column("FirstName")]
[Display(Name = "First Name")]
public string FirstMidName { get; set; }
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
[Display(Name = "Enrollment Date")]
public DateTime EnrollmentDate { get; set; }
[Display(Name = "Full Name")]
public string FullName
{
get
{
return LastName + ", " + FirstMidName;
}
}
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
}
Das erforderliche Attribut
Das Attribut "Required " macht die Nameneigenschaften erforderlich. Dies Required attribute
ist für Werttypen wie DateTime, Int, Double und Float nicht erforderlich. Werttypen können keine NULL-Werte zugewiesen werden, daher werden sie inhärent als erforderliche Felder behandelt.
Das Required
-Attribut muss mit MinimumLength
verwendet werden, damit die MinimumLength
erzwungen wird.
[Display(Name = "Last Name")]
[Required]
[StringLength(50, MinimumLength=2)]
public string LastName { get; set; }
MinimumLength
und Required
lassen Leerzeichen zu, um die Überprüfung zu bestehen. Verwenden Sie das RegularExpression
Attribut für die vollständige Kontrolle über die Zeichenfolge.
Das Display-Attribut
Das Display
-Attribut gibt an, dass die Beschriftung der Textfelder in jeder Instanz „First Name“, „Last Name“, „Full Name“ und „Enrollment Date“ lauten soll, statt dem Eigenschaftsnamen zu entsprechen (bei dem die Wörter nicht durch Leerräume getrennt werden).
Die berechnete FullName-Eigenschaft
Bei FullName
handelt es sich um eine berechnete Eigenschaft, die einen Wert zurückgibt, der durch das Verketten von zwei weiteren Eigenschaften erstellt wird. Daher verfügt sie nur über einen get
Accessor, und in der Datenbank wird keine FullName
Spalte generiert.
Erstellen der Entität „Instructor“
Erstellen Sie Modelle\Instructor.cs, und ersetzen Sie den Vorlagencode durch den folgenden Code:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace ContosoUniversity.Models
{
public class Instructor
{
public int ID { get; set; }
[Required]
[Display(Name = "Last Name")]
[StringLength(50)]
public string LastName { get; set; }
[Required]
[Column("FirstName")]
[Display(Name = "First Name")]
[StringLength(50)]
public string FirstMidName { get; set; }
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
[Display(Name = "Hire Date")]
public DateTime HireDate { get; set; }
[Display(Name = "Full Name")]
public string FullName
{
get { return LastName + ", " + FirstMidName; }
}
public virtual ICollection<Course> Courses { get; set; }
public virtual OfficeAssignment OfficeAssignment { get; set; }
}
}
Beachten Sie, dass einige Eigenschaften in den Entitäten Student
und Instructor
identisch sind. Im folgenden Tutorial Implementing Inheritance (Implementierung von Vererbung) gestalten Sie diesen Code um, um die Redundanzen zu entfernen.
Sie können mehrere Attribute in eine Zeile setzen, sodass Sie auch den Kursleiter wie folgt schreiben können:
public class Instructor
{
public int ID { get; set; }
[Display(Name = "Last Name"),StringLength(50, MinimumLength=1)]
public string LastName { get; set; }
[Column("FirstName"),Display(Name = "First Name"),StringLength(50, MinimumLength=1)]
public string FirstMidName { get; set; }
[DataType(DataType.Date),Display(Name = "Hire Date"),DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime HireDate { get; set; }
[Display(Name = "Full Name")]
public string FullName
{
get { return LastName + ", " + FirstMidName; }
}
public virtual ICollection<Course> Courses { get; set; }
public virtual OfficeAssignment OfficeAssignment { get; set; }
}
Die Kurs- und OfficeAssignment-Navigationseigenschaften
Bei den Eigenschaften Courses
und OfficeAssignment
handelt es sich um Navigationseigenschaften. Wie bereits erläutert, werden sie in der Regel als virtuell definiert, sodass sie ein Entity Framework-Feature nutzen können, das als faules Laden bezeichnet wird. Wenn eine Navigationseigenschaft mehrere Entitäten enthalten kann, muss der Typ die ICollection<T-Schnittstelle> implementieren. Beispiel: IList<T> qualifiziert, aber nicht IEnumerable<T>, da IEnumerable<T>
Add nicht implementiert wird.
Ein Kursleiter kann eine beliebige Anzahl von Kursen unterrichten, also Courses
als Eine Sammlung von Course
Entitäten definiert.
public virtual ICollection<Course> Courses { get; set; }
Unsere Geschäftsregeln geben an, dass ein Kursleiter höchstens ein Büro haben kann. Dies ist also OfficeAssignment
als einzelne OfficeAssignment
Entität definiert (dies kann sein null
, wenn kein Büro zugewiesen ist).
public virtual OfficeAssignment OfficeAssignment { get; set; }
Erstellen der Entität „OfficeAssignment“
Erstellen Sie Modelle\OfficeAssignment.cs mit dem folgenden Code:
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace ContosoUniversity.Models
{
public class OfficeAssignment
{
[Key]
[ForeignKey("Instructor")]
public int InstructorID { get; set; }
[StringLength(50)]
[Display(Name = "Office Location")]
public string Location { get; set; }
public virtual Instructor Instructor { get; set; }
}
}
Erstellen Sie das Projekt, das Ihre Änderungen speichert und überprüft, ob Sie keine Kopier- und Einfügefehler vorgenommen haben, die der Compiler abfangen kann.
Das Key-Attribut
Es besteht 1:0- oder 1:1-Beziehung zwischen den Entitäten Instructor
und OfficeAssignment
. Eine Bürozuweisung ist nur in Beziehung zu der Lehrkraft vorhanden, der sie zugewiesen ist. Daher ist deren Primärschlüssel auch der Fremdschlüssel für die Entität Instructor
. Das Entity Framework kann jedoch nicht automatisch als Primärschlüssel dieser Entität erkannt werden InstructorID
, da der Name nicht der ID
Benennungskonvention für Klassennamen ID
entspricht. Deshalb wird das Key
-Attribut verwendet, um „InstructorID“ als Schlüssel zu erkennen:
[Key]
[ForeignKey("Instructor")]
public int InstructorID { get; set; }
Sie können das Key
Attribut auch verwenden, wenn die Entität über einen eigenen Primärschlüssel verfügt, aber Sie die Eigenschaft anders benennen möchten als classnameID
oder ID
. Standardmäßig behandelt EF den Schlüssel als nicht datenbankgeneriert, da die Spalte für eine identifizierende Beziehung bestimmt ist.
Das ForeignKey-Attribut
Wenn eine 1:0-oder-1-Beziehung oder eine 1:1-Beziehung zwischen zwei Entitäten (z. B. zwischen OfficeAssignment
und Instructor
) vorhanden ist, kann EF nicht herausfinden, welches Ende der Beziehung der Prinzipal und welches Ende abhängig ist. 1:1-Beziehungen weisen eine Referenznavigationseigenschaft in jeder Klasse auf die andere Klasse auf. Das ForeignKey-Attribut kann auf die abhängige Klasse angewendet werden, um die Beziehung herzustellen. Wenn Sie das ForeignKey-Attribut weglassen, wird beim Versuch, die Migration zu erstellen, die folgende Fehlermeldung angezeigt:
Das Hauptende einer Zuordnung zwischen den Typen "ContosoUniversity.Models.OfficeAssignment" und "ContosoUniversity.Models.Instructor" kann nicht ermittelt werden. Das Prinzipalende dieser Zuordnung muss explizit mithilfe der Beziehungs-Fluent-API oder Datenanmerkungen konfiguriert werden.
Später im Lernprogramm erfahren Sie, wie Sie diese Beziehung mit der Fluent-API konfigurieren.
Die Instructor Navigation-Eigenschaft
Die Instructor
Entität verfügt über eine nullfähige OfficeAssignment
Navigationseigenschaft (da ein Kursleiter möglicherweise keine Bürozuweisung hat), und die OfficeAssignment
Entität verfügt über eine nicht nullable Instructor
Navigationseigenschaft (da eine Bürozuweisung nicht ohne Einen Kursleiter InstructorID
vorhanden sein kann - ist nicht nullable). Wenn eine Instructor
Entität über eine zugehörige OfficeAssignment
Entität verfügt, weist jede Entität einen Verweis auf das andere in der Navigationseigenschaft auf.
Sie können ein [Required]
Attribut für die Navigationseigenschaft "Instructor" einfügen, um anzugeben, dass ein verwandter Kursleiter vorhanden sein muss, aber Sie müssen dies nicht tun, da der Fremdschlüssel "InstructorID" (der auch der Schlüssel zu dieser Tabelle ist) nicht nullfähig ist.
Ändern der Kursentität
Ersetzen Sie in "Models\Course.cs" den Code, den Sie zuvor hinzugefügt haben, durch den folgenden Code:
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace ContosoUniversity.Models
{
public class Course
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Display(Name = "Number")]
public int CourseID { get; set; }
[StringLength(50, MinimumLength = 3)]
public string Title { get; set; }
[Range(0, 5)]
public int Credits { get; set; }
public int DepartmentID { get; set; }
public virtual Department Department { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
public virtual ICollection<Instructor> Instructors { get; set; }
}
}
Die Kursentität verfügt über eine Fremdschlüsseleigenschaft DepartmentID
, die auf die zugehörige Department
Entität verweist und über eine Department
Navigationseigenschaft verfügt. Entity Framework erfordert nicht, dass Sie eine Fremdschlüsseleigenschaft zu Ihrem Datenmodell hinzufügen, wenn eine Navigationseigenschaft für eine verknüpfte Entität vorhanden ist. EF erstellt automatisch Fremdschlüssel in der Datenbank, wo sie benötigt werden. Durch Fremdschlüssel im Datenmodell können Updates einfacher und effizienter durchgeführt werden. Wenn Sie z. B. eine Kursentität zum Bearbeiten abrufen, ist die Department
Entität null, wenn Sie sie nicht laden. Wenn Sie die Kursentität aktualisieren, müssen Sie die Department
Entität zuerst abrufen. Wenn die Fremdschlüsseleigenschaft DepartmentID
im Datenmodell vorhanden ist, müssen Sie die Entität Department
vor dem Update nicht abrufen.
Das DatabaseGenerated-Attribut
Das DatabaseGenerated-Attribut mit dem Parameter None für die CourseID
Eigenschaft gibt an, dass Primärschlüsselwerte vom Benutzer bereitgestellt werden, anstatt von der Datenbank generiert zu werden.
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Display(Name = "Number")]
public int CourseID { get; set; }
Standardmäßig geht das Entity Framework davon aus, dass Primärschlüsselwerte von der Datenbank generiert werden. Das ist in den meisten Fällen erwünscht. Für Course
-Entitäten verwenden Sie jedoch eine benutzerdefinierte Kursnummer, z. B. eine 1000er-Reihe für eine Abteilung, eine 2000er-Reihe für eine andere – und so weiter.
Fremdschlüssel- und Navigationseigenschaften
Die Fremdschlüssel- und Navigationseigenschaften in der Entität Course
stellen folgende Beziehungen dar:
Ein Kurs ist einer Abteilung zugeordnet, sodass es aus den oben genannten Gründen eine
DepartmentID
-Fremdschlüsseleigenschaft und eineDepartment
-Navigationseigenschaft gibt.public int DepartmentID { get; set; } public virtual Department Department { get; set; }
In einem Kurs können beliebig viele Studenten angemeldet sein, darum stellt die
Enrollments
-Navigationseigenschaft eine Auflistung dar:public virtual ICollection<Enrollment> Enrollments { get; set; }
Ein Kurs kann von mehreren Dozenten unterrichtet werden, darum stellt die
Instructors
-Navigationseigenschaft eine Auflistung dar:public virtual ICollection<Instructor> Instructors { get; set; }
Erstellen der Entität „Department“
Erstellen Sie Modelle\Department.cs mit dem folgenden Code:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace ContosoUniversity.Models
{
public class Department
{
public int DepartmentID { get; set; }
[StringLength(50, MinimumLength=3)]
public string Name { get; set; }
[DataType(DataType.Currency)]
[Column(TypeName = "money")]
public decimal Budget { get; set; }
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
[Display(Name = "Start Date")]
public DateTime StartDate { get; set; }
public int? InstructorID { get; set; }
public virtual Instructor Administrator { get; set; }
public virtual ICollection<Course> Courses { get; set; }
}
}
Das Column-Attribut
Zuvor haben Sie das Column-Attribut verwendet, um die Spaltennamenszuordnung zu ändern. Im Code für die Department
Entität wird das Attribut verwendet, um die Column
SQL-Datentypzuordnung so zu ändern, dass die Spalte mithilfe des SQL Server-Geldtyps in der Datenbank definiert wird:
[Column(TypeName="money")]
public decimal Budget { get; set; }
Die Spaltenzuordnung ist im Allgemeinen nicht erforderlich, da das Entity Framework normalerweise den entsprechenden SQL Server-Datentyp basierend auf dem CLR-Typ auswählt, den Sie für die Eigenschaft definieren. Der CLR-Typ decimal
wird dem SQL Server-Typ decimal
zugeordnet. In diesem Fall wissen Sie jedoch, dass die Spalte Währungsbeträge enthält, und der Datentyp "Geld " ist dafür besser geeignet. Weitere Informationen zu CLR-Datentypen und deren Übereinstimmung mit SQL Server-Datentypen finden Sie unter SqlClient für Entity FrameworkTypes.
Fremdschlüssel- und Navigationseigenschaften
Die Fremdschlüssel- und Navigationseigenschaften stellen folgende Beziehungen dar:
Eine Abteilung kann einen Administrator besitzen oder nicht, und bei einem Administrator handelt es sich immer um einen Dozenten. Daher wird die
InstructorID
Eigenschaft als Fremdschlüssel für dieInstructor
Entität eingeschlossen, und nach derint
Typbezeichnung wird ein Fragezeichen hinzugefügt, um die Eigenschaft als Nullwerte zu kennzeichnen. Die Navigationseigenschaft wird benanntAdministrator
, enthält jedoch eineInstructor
Entität:public int? InstructorID { get; set; } public virtual Instructor Administrator { get; set; }
Eine Abteilung verfügt möglicherweise über viele Kurse, daher gibt es eine
Courses
Navigationseigenschaft:public virtual ICollection<Course> Courses { get; set; }
Hinweis
Gemäß der Konvention aktiviert Entity Framework das kaskadierende Delete für nicht auf NULL festlegbare Fremdschlüssel und für m:n-Beziehungen. Dies kann zu zirkulären Regeln für kaskadierende Deletes führen, wodurch eine Ausnahme ausgelöst wird, wenn Sie versuchen, eine Migration hinzuzufügen. Wenn Sie die
Department.InstructorID
Eigenschaft z. B. nicht als Nullwerte definiert haben, erhalten Sie die folgende Ausnahmemeldung: "Die referenzielle Beziehung führt zu einem zyklischen Verweis, der nicht zulässig ist." Wenn Für Ihre Geschäftsregeln eine Eigenschaft erforderlichInstructorID
ist, die nicht nullwertebar ist, müssen Sie die folgende Fluent-API-Anweisung verwenden, um die Löschweitergabe für die Beziehung zu deaktivieren:
modelBuilder.Entity().HasRequired(d => d.Administrator).WithMany().WillCascadeOnDelete(false);
Ändern der Entität „Enrollment“
Ersetzen Sie in "Models\Enrollment.cs" den Code, den Sie zuvor hinzugefügt haben, durch den folgenden Code.
using System.ComponentModel.DataAnnotations;
namespace ContosoUniversity.Models
{
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; }
[DisplayFormat(NullDisplayText = "No grade")]
public Grade? Grade { get; set; }
public virtual Course Course { get; set; }
public virtual Student Student { get; set; }
}
}
Fremdschlüssel- und Navigationseigenschaften
Die Fremdschlüssel- und Navigationseigenschaften stellen folgende Beziehungen dar:
Ein Anmeldungsdatensatz gilt für einen einzelnen Kurs, sodass es eine
CourseID
-Fremdschlüsseleigenschaft und eineCourse
-Navigationseigenschaft gibt:public int CourseID { get; set; } public virtual Course Course { get; set; }
Ein Anmeldungsdatensatz gilt für einen einzelnen Studenten, sodass es eine
StudentID
-Fremdschlüsseleigenschaft und eineStudent
-Navigationseigenschaft gibt:public int StudentID { get; set; } public virtual Student Student { get; set; }
n:n-Beziehungen
Es besteht eine m:n-Beziehung zwischen den Entitäten Student
und Course
, und die Enrollment
-Entitätsfunktionen liegen als m:n-Jointabelle mit Nutzdaten in der Datenbank vor. Dies bedeutet, dass die Enrollment
Tabelle neben Fremdschlüsseln für die verknüpften Tabellen zusätzliche Daten enthält (in diesem Fall ein Primärschlüssel und eine Grade
Eigenschaft).
Die folgende Abbildung stellt dar, wie diese Beziehungen in einem Entitätsdiagramm aussehen. (Dieses Diagramm wurde mithilfe der Entity Framework Power Tools; das Erstellen des Diagramms ist nicht Teil des Lernprogramms, es wird nur hier als Abbildung verwendet.)
Jede Beziehung weist an einem Ende „1“ und am anderen Ende „*“ auf, wodurch eine 1:n-Beziehung dargestellt wird.
Wenn in der Tabelle Enrollment
nicht die Grade-Information enthalten wäre, müsste diese nur die beiden Fremdschlüssel (CourseID
und StudentID
) enthalten. In diesem Fall entspricht sie einer n:n-Verknüpfungstabelle ohne Nutzlast (oder eine reine Verknüpfungstabelle) in der Datenbank, und Sie müssten überhaupt keine Modellklasse dafür erstellen. Die Instructor
Entitäten Course
haben diese Art von n:n-Beziehung, und wie Sie sehen können, gibt es keine Entitätsklasse zwischen ihnen:
Eine Verknüpfungstabelle ist jedoch in der Datenbank erforderlich, wie im folgenden Datenbankdiagramm dargestellt:
Das Entity Framework erstellt die CourseInstructor
Tabelle automatisch, und Sie lesen und aktualisieren sie indirekt, indem Sie die Instructor.Courses
Eigenschaften Course.Instructors
und Navigationseigenschaften lesen und aktualisieren.
Entitätsbeziehungsdiagramm
Die folgende Abbildung stellt das Diagramm dar, das von Entity Framework Power Tools für das vollständige Modell „School“ erstellt wird.
Neben den n:n-Beziehungslinien (* zu *) und den 1:n-Beziehungslinien (1 bis *) können Sie hier die 1:0-1-Beziehungslinie (1 bis 0,.1) zwischen den Instructor
Entitäten und OfficeAssignment
den Entitäten und der 1:1:n-Beziehungslinie (0,.1 bis *) zwischen den Entitäten "Ausbilder" und "Abteilung" sehen.
Hinzufügen von Code zum Datenbankkontext
Als Nächstes fügen Sie der Klasse die neuen Entitäten hinzu SchoolContext
und passen einige der Zuordnungen mithilfe von Fluent-API-Aufrufen an. Die API ist "fluent", da sie häufig durch Zeichenfolgen einer Reihe von Methodenaufrufen in eine einzelne Anweisung verwendet wird, wie im folgenden Beispiel gezeigt:
modelBuilder.Entity<Course>()
.HasMany(c => c.Instructors).WithMany(i => i.Courses)
.Map(t => t.MapLeftKey("CourseID")
.MapRightKey("InstructorID")
.ToTable("CourseInstructor"));
In diesem Lernprogramm verwenden Sie die Fluent-API nur für die Datenbankzuordnung, die Sie nicht mit Attributen ausführen können. Sie können die Fluent-API jedoch ebenfalls verwenden, um den Großteil der Regeln für die Formatierung, Validierung und Zuordnung anzugeben, die Sie mithilfe von Attributen festlegen können. Manche Attribute, z.B. MinimumLength
, können nicht mit der Fluent-API angewendet werden. Wie bereits erwähnt, MinimumLength
ändert das Schema nicht, es wendet nur eine client- und serverseitige Gültigkeitsprüfungsregel an.
Manche Entwickler*innen bevorzugen es, nur eine Fluent-API zu verwenden, sodass sie ihre Entitätsklassen „sauber“ halten können. Sie können Attribute und die Fluent-API mischen, wenn Sie möchten. Einige Anpassungen können nur mithilfe der Fluent-API vorgenommen werden, im Allgemeinen wird jedoch empfohlen, einen der beiden Ansätze auszuwählen und diesen so konsistent wie möglich zu verwenden.
Ersetzen Sie den Code in DAL\SchoolContext.cs durch den folgenden Code, um dem Datenmodell die neuen Entitäten hinzuzufügen und eine Datenbankzuordnung durchzuführen, die Sie nicht mithilfe von Attributen ausgeführt haben:
using ContosoUniversity.Models;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;
namespace ContosoUniversity.DAL
{
public class SchoolContext : DbContext
{
public DbSet<Course> Courses { get; set; }
public DbSet<Department> Departments { get; set; }
public DbSet<Enrollment> Enrollments { get; set; }
public DbSet<Instructor> Instructors { get; set; }
public DbSet<Student> Students { get; set; }
public DbSet<OfficeAssignment> OfficeAssignments { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Course>()
.HasMany(c => c.Instructors).WithMany(i => i.Courses)
.Map(t => t.MapLeftKey("CourseID")
.MapRightKey("InstructorID")
.ToTable("CourseInstructor"));
}
}
}
Die neue Anweisung in der OnModelCreating-Methode konfiguriert die n:n-Verknüpfungstabelle:
Für die m:n-Beziehung zwischen den
Instructor
Und-EntitätenCourse
gibt der Code die Tabellen- und Spaltennamen für die Verknüpfungstabelle an. Code First kann die m:n-Beziehung für Sie ohne diesen Code konfigurieren. Wenn Sie dies jedoch nicht aufrufen, erhalten Sie Standardnamen wieInstructorInstructorID
z. B. für dieInstructorID
Spalte.modelBuilder.Entity<Course>() .HasMany(c => c.Instructors).WithMany(i => i.Courses) .Map(t => t.MapLeftKey("CourseID") .MapRightKey("InstructorID") .ToTable("CourseInstructor"));
Der folgende Code enthält ein Beispiel dafür, wie Sie die Fluent-API anstelle von Attributen verwendet haben könnten, um die Beziehung zwischen den Entitäten und OfficeAssignment
den Instructor
Entitäten anzugeben:
modelBuilder.Entity<Instructor>()
.HasOptional(p => p.OfficeAssignment).WithRequired(p => p.Instructor);
Informationen dazu, welche "fluent API"-Anweisungen hinter den Kulissen ausführen, finden Sie im Blogbeitrag der Fluent-API .
Füllen der Datenbank mit Testdaten
Ersetzen Sie den Code in der Datei "Migration\Configuration.cs " durch den folgenden Code, um Seeddaten für die neuen Entitäten bereitzustellen, die Sie erstellt haben.
namespace ContosoUniversity.Migrations
{
using ContosoUniversity.Models;
using ContosoUniversity.DAL;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Migrations;
using System.Linq;
internal sealed class Configuration : DbMigrationsConfiguration<SchoolContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
}
protected override void Seed(SchoolContext context)
{
var students = new List<Student>
{
new Student { FirstMidName = "Carson", LastName = "Alexander",
EnrollmentDate = DateTime.Parse("2010-09-01") },
new Student { FirstMidName = "Meredith", LastName = "Alonso",
EnrollmentDate = DateTime.Parse("2012-09-01") },
new Student { FirstMidName = "Arturo", LastName = "Anand",
EnrollmentDate = DateTime.Parse("2013-09-01") },
new Student { FirstMidName = "Gytis", LastName = "Barzdukas",
EnrollmentDate = DateTime.Parse("2012-09-01") },
new Student { FirstMidName = "Yan", LastName = "Li",
EnrollmentDate = DateTime.Parse("2012-09-01") },
new Student { FirstMidName = "Peggy", LastName = "Justice",
EnrollmentDate = DateTime.Parse("2011-09-01") },
new Student { FirstMidName = "Laura", LastName = "Norman",
EnrollmentDate = DateTime.Parse("2013-09-01") },
new Student { FirstMidName = "Nino", LastName = "Olivetto",
EnrollmentDate = DateTime.Parse("2005-09-01") }
};
students.ForEach(s => context.Students.AddOrUpdate(p => p.LastName, s));
context.SaveChanges();
var instructors = new List<Instructor>
{
new Instructor { FirstMidName = "Kim", LastName = "Abercrombie",
HireDate = DateTime.Parse("1995-03-11") },
new Instructor { FirstMidName = "Fadi", LastName = "Fakhouri",
HireDate = DateTime.Parse("2002-07-06") },
new Instructor { FirstMidName = "Roger", LastName = "Harui",
HireDate = DateTime.Parse("1998-07-01") },
new Instructor { FirstMidName = "Candace", LastName = "Kapoor",
HireDate = DateTime.Parse("2001-01-15") },
new Instructor { FirstMidName = "Roger", LastName = "Zheng",
HireDate = DateTime.Parse("2004-02-12") }
};
instructors.ForEach(s => context.Instructors.AddOrUpdate(p => p.LastName, s));
context.SaveChanges();
var departments = new List<Department>
{
new Department { Name = "English", Budget = 350000,
StartDate = DateTime.Parse("2007-09-01"),
InstructorID = instructors.Single( i => i.LastName == "Abercrombie").ID },
new Department { Name = "Mathematics", Budget = 100000,
StartDate = DateTime.Parse("2007-09-01"),
InstructorID = instructors.Single( i => i.LastName == "Fakhouri").ID },
new Department { Name = "Engineering", Budget = 350000,
StartDate = DateTime.Parse("2007-09-01"),
InstructorID = instructors.Single( i => i.LastName == "Harui").ID },
new Department { Name = "Economics", Budget = 100000,
StartDate = DateTime.Parse("2007-09-01"),
InstructorID = instructors.Single( i => i.LastName == "Kapoor").ID }
};
departments.ForEach(s => context.Departments.AddOrUpdate(p => p.Name, s));
context.SaveChanges();
var courses = new List<Course>
{
new Course {CourseID = 1050, Title = "Chemistry", Credits = 3,
DepartmentID = departments.Single( s => s.Name == "Engineering").DepartmentID,
Instructors = new List<Instructor>()
},
new Course {CourseID = 4022, Title = "Microeconomics", Credits = 3,
DepartmentID = departments.Single( s => s.Name == "Economics").DepartmentID,
Instructors = new List<Instructor>()
},
new Course {CourseID = 4041, Title = "Macroeconomics", Credits = 3,
DepartmentID = departments.Single( s => s.Name == "Economics").DepartmentID,
Instructors = new List<Instructor>()
},
new Course {CourseID = 1045, Title = "Calculus", Credits = 4,
DepartmentID = departments.Single( s => s.Name == "Mathematics").DepartmentID,
Instructors = new List<Instructor>()
},
new Course {CourseID = 3141, Title = "Trigonometry", Credits = 4,
DepartmentID = departments.Single( s => s.Name == "Mathematics").DepartmentID,
Instructors = new List<Instructor>()
},
new Course {CourseID = 2021, Title = "Composition", Credits = 3,
DepartmentID = departments.Single( s => s.Name == "English").DepartmentID,
Instructors = new List<Instructor>()
},
new Course {CourseID = 2042, Title = "Literature", Credits = 4,
DepartmentID = departments.Single( s => s.Name == "English").DepartmentID,
Instructors = new List<Instructor>()
},
};
courses.ForEach(s => context.Courses.AddOrUpdate(p => p.CourseID, s));
context.SaveChanges();
var officeAssignments = new List<OfficeAssignment>
{
new OfficeAssignment {
InstructorID = instructors.Single( i => i.LastName == "Fakhouri").ID,
Location = "Smith 17" },
new OfficeAssignment {
InstructorID = instructors.Single( i => i.LastName == "Harui").ID,
Location = "Gowan 27" },
new OfficeAssignment {
InstructorID = instructors.Single( i => i.LastName == "Kapoor").ID,
Location = "Thompson 304" },
};
officeAssignments.ForEach(s => context.OfficeAssignments.AddOrUpdate(p => p.InstructorID, s));
context.SaveChanges();
AddOrUpdateInstructor(context, "Chemistry", "Kapoor");
AddOrUpdateInstructor(context, "Chemistry", "Harui");
AddOrUpdateInstructor(context, "Microeconomics", "Zheng");
AddOrUpdateInstructor(context, "Macroeconomics", "Zheng");
AddOrUpdateInstructor(context, "Calculus", "Fakhouri");
AddOrUpdateInstructor(context, "Trigonometry", "Harui");
AddOrUpdateInstructor(context, "Composition", "Abercrombie");
AddOrUpdateInstructor(context, "Literature", "Abercrombie");
context.SaveChanges();
var enrollments = new List<Enrollment>
{
new Enrollment {
StudentID = students.Single(s => s.LastName == "Alexander").ID,
CourseID = courses.Single(c => c.Title == "Chemistry" ).CourseID,
Grade = Grade.A
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Alexander").ID,
CourseID = courses.Single(c => c.Title == "Microeconomics" ).CourseID,
Grade = Grade.C
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Alexander").ID,
CourseID = courses.Single(c => c.Title == "Macroeconomics" ).CourseID,
Grade = Grade.B
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Alonso").ID,
CourseID = courses.Single(c => c.Title == "Calculus" ).CourseID,
Grade = Grade.B
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Alonso").ID,
CourseID = courses.Single(c => c.Title == "Trigonometry" ).CourseID,
Grade = Grade.B
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Alonso").ID,
CourseID = courses.Single(c => c.Title == "Composition" ).CourseID,
Grade = Grade.B
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Anand").ID,
CourseID = courses.Single(c => c.Title == "Chemistry" ).CourseID
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Anand").ID,
CourseID = courses.Single(c => c.Title == "Microeconomics").CourseID,
Grade = Grade.B
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Barzdukas").ID,
CourseID = courses.Single(c => c.Title == "Chemistry").CourseID,
Grade = Grade.B
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Li").ID,
CourseID = courses.Single(c => c.Title == "Composition").CourseID,
Grade = Grade.B
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Justice").ID,
CourseID = courses.Single(c => c.Title == "Literature").CourseID,
Grade = Grade.B
}
};
foreach (Enrollment e in enrollments)
{
var enrollmentInDataBase = context.Enrollments.Where(
s =>
s.Student.ID == e.StudentID &&
s.Course.CourseID == e.CourseID).SingleOrDefault();
if (enrollmentInDataBase == null)
{
context.Enrollments.Add(e);
}
}
context.SaveChanges();
}
void AddOrUpdateInstructor(SchoolContext context, string courseTitle, string instructorName)
{
var crs = context.Courses.SingleOrDefault(c => c.Title == courseTitle);
var inst = crs.Instructors.SingleOrDefault(i => i.LastName == instructorName);
if (inst == null)
crs.Instructors.Add(context.Instructors.Single(i => i.LastName == instructorName));
}
}
}
Wie Sie im ersten Lernprogramm gesehen haben, aktualisiert der Großteil dieses Codes einfach neue Entitätsobjekte oder erstellt neue Entitätsobjekte und lädt Beispieldaten nach Bedarf zum Testen in Eigenschaften. Beachten Sie jedoch, wie die Course
Entität, die eine n:n-Beziehung mit der Instructor
Entität aufweist, behandelt wird:
var courses = new List<Course>
{
new Course {CourseID = 1050, Title = "Chemistry", Credits = 3,
DepartmentID = departments.Single( s => s.Name == "Engineering").DepartmentID,
Instructors = new List<Instructor>()
},
...
};
courses.ForEach(s => context.Courses.AddOrUpdate(p => p.CourseID, s));
context.SaveChanges();
Wenn Sie ein Course
Objekt erstellen, initialisieren Sie die Instructors
Navigationseigenschaft mithilfe des Codes Instructors = new List<Instructor>()
als leere Auflistung. Dadurch können Mithilfe der Methode Entitäten hinzugefügt Instructor
werden, die mit dieser Course
Instructors.Add
verknüpft sind. Wenn Sie keine leere Liste erstellt haben, können Sie diese Beziehungen nicht hinzufügen, da die Instructors
Eigenschaft null wäre und keine Methode hätte Add
. Sie können dem Konstruktor auch die Listeninitialisierung hinzufügen.
Hinzufügen einer Migration
Geben Sie im PMC den add-migration
Befehl ein (führen Sie den update-database
Befehl noch nicht aus):
add-Migration ComplexDataModel
Wenn Sie versucht haben, den Befehl update-database
zu diesem Zeitpunkt auszuführen (führen Sie diesen noch nicht aus), erhalten Sie folgende Fehlermeldung:
Die ALTER TABLE-Anweisung steht in Konflikt mit der FOREIGN KEY-Einschränkung „FK_dbo.Course_dbo.Department_DepartmentID“. Der Konflikt trat in der „ContosoUniversity“-Datenbank, Tabelle „dbo.Department“, Spalte „DepartmentID“ auf.
Manchmal müssen Sie beim Ausführen von Migrationen mit vorhandenen Daten Stubdaten in die Datenbank einfügen, um Fremdschlüsseleinschränkungen zu erfüllen, und das müssen Sie jetzt tun. Der generierte Code in der ComplexDataModel-Methode Up
fügt der Course
Tabelle einen nicht nullablen DepartmentID
Fremdschlüssel hinzu. Da beim Ausführen des Codes bereits Zeilen in der Course
Tabelle vorhanden sind, schlägt der AddColumn
Vorgang fehl, da SQL Server nicht weiß, welcher Wert in die Spalte eingefügt werden soll, die nicht null sein kann. Daher müssen Sie den Code ändern, um der neuen Spalte einen Standardwert zu geben, und erstellen Sie eine Stubabteilung mit dem Namen "Temp", um als Standardabteilung zu fungieren. Daher werden vorhandene Course
Zeilen nach ausführung der Up
Methode mit der Abteilung "Temp" verknüpft. Sie können sie mit den richtigen Abteilungen in der Seed
Methode verknüpfen.
Bearbeiten Sie den <Zeitstempel>_ComplexDataModel.cs Datei, kommentieren Sie die Codezeile aus, in der die Spalte "DepartmentID" zur Tabelle "Kurs" hinzugefügt wird, und fügen Sie den folgenden hervorgehobenen Code hinzu (die kommentierte Zeile ist ebenfalls hervorgehoben):
CreateTable(
"dbo.CourseInstructor",
c => new
{
CourseID = c.Int(nullable: false),
InstructorID = c.Int(nullable: false),
})
.PrimaryKey(t => new { t.CourseID, t.InstructorID })
.ForeignKey("dbo.Course", t => t.CourseID, cascadeDelete: true)
.ForeignKey("dbo.Instructor", t => t.InstructorID, cascadeDelete: true)
.Index(t => t.CourseID)
.Index(t => t.InstructorID);
// Create a department for course to point to.
Sql("INSERT INTO dbo.Department (Name, Budget, StartDate) VALUES ('Temp', 0.00, GETDATE())");
// default value for FK points to department created above.
AddColumn("dbo.Course", "DepartmentID", c => c.Int(nullable: false, defaultValue: 1));
//AddColumn("dbo.Course", "DepartmentID", c => c.Int(nullable: false));
AlterColumn("dbo.Course", "Title", c => c.String(maxLength: 50));
Wenn die Seed
Methode ausgeführt wird, fügt sie Zeilen in die Department
Tabelle ein, und es bezieht sich auf vorhandene Course
Zeilen zu diesen neuen Department
Zeilen. Wenn Sie keine Kurse in der Benutzeroberfläche hinzugefügt haben, benötigen Sie die Abteilung "Temp" oder den Standardwert in der Course.DepartmentID
Spalte nicht mehr. Um die Möglichkeit zu ermöglichen, dass jemand Kurse mithilfe der Anwendung hinzugefügt hat, sollten Sie auch den Seed
Methodencode aktualisieren, um sicherzustellen, dass alle Course
Zeilen (nicht nur die von früheren Läufen der Seed
Methode eingefügten) gültige DepartmentID
Werte haben, bevor Sie den Standardwert aus der Spalte entfernen und die Abteilung "Temp" löschen.
Aktualisieren der Datenbank
Nachdem Sie die Bearbeitung des <Zeitstempels> abgeschlossen haben_ComplexDataModel.cs Datei, geben Sie den update-database
Befehl in der PMC-Datei ein, um die Migration auszuführen.
update-database
Hinweis
Es ist möglich, andere Fehler beim Migrieren von Daten zu erhalten und Schemaänderungen vorzunehmen. Wenn Sie Migrationsfehler erhalten, die Sie nicht beheben können, können Sie den Datenbanknamen in der Verbindungszeichenfolge ändern oder die Datenbank löschen. Der einfachste Ansatz besteht darin, die Datenbank in der Datei "Web.config " umzubenennen. Im folgenden Beispiel wird der Name in CU_Test geändert:
<add name="SchoolContext" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=CU_Test;Integrated Security=SSPI;"
providerName="System.Data.SqlClient" />
Bei einer neuen Datenbank gibt es keine Zu migrierenden Daten, und der update-database
Befehl wird wahrscheinlicher ohne Fehler abgeschlossen. Anweisungen zum Löschen der Datenbank finden Sie unter How to Drop a Database from Visual Studio 2012.
Wenn dies fehlschlägt, können Sie versuchen, die Datenbank erneut zu initialisieren, indem Sie den folgenden Befehl in das PMC eingeben:
update-database -TargetMigration:0
Öffnen Sie die Datenbank wie zuvor im Server-Explorer , und erweitern Sie den Tabellenknoten , um zu sehen, dass alle Tabellen erstellt wurden. (Wenn Sie immer noch Der Server-Explorer wird aus der früheren Zeit geöffnet, und klicken Sie auf die Schaltfläche "Aktualisieren ".)
Sie haben keine Modellklasse für die CourseInstructor
Tabelle erstellt. Wie bereits erläutert, ist dies eine Verknüpfungstabelle für die m:n-Beziehung zwischen den Instructor
Und-Entitäten Course
.
Klicken Sie mit der rechten Maustaste auf die CourseInstructor
Tabelle, und wählen Sie " Tabellendaten anzeigen" aus, um zu überprüfen, ob sie daten als Ergebnis der Instructor
Entitäten enthält, die Sie der Course.Instructors
Navigationseigenschaft hinzugefügt haben.
Abrufen des Codes
Abgeschlossenes Projekt herunterladen
Zusätzliche Ressourcen
Links zu anderen Entity Framework-Ressourcen finden Sie im ASP.NET Datenzugriff – Empfohlene Ressourcen.
Nächste Schritte
In diesem Tutorial:
- Angepasstes Datenmodell
- Aktualisierte Schülerentität
- Entität „Instructor“ wurde erstellt
- Entität „OfficeAssignment“ wurde erstellt
- Die Kursentität wurde geändert.
- Die Abteilungsentität wurde erstellt.
- Die Registrierungsentität wurde geändert.
- Code zum Datenbankkontext hinzugefügt
- Datenbank wurde mit Testdaten gefüllt
- Migration wurde hinzugefügt
- Datenbank wurde aktualisiert
Wechseln Sie zum nächsten Artikel, um zu erfahren, wie Sie verwandte Daten lesen und anzeigen, die das Entity Framework in Navigationseigenschaften lädt.