Erstellen eines komplexeren Datenmodells für eine ASP.NET MVC-Anwendung (4 von 10)
Von Tom Dykstra
Die Contoso University-Beispielwebanwendung veranschaulicht, wie ASP.NET MVC 4-Anwendungen mithilfe von Entity Framework 5 Code First und Visual Studio 2012 erstellt werden. Informationen zu dieser Tutorialreihe finden Sie im ersten Tutorial der Reihe.
Hinweis
Wenn ein Problem auftritt, das Sie nicht beheben können, laden Sie das fertige Kapitel herunter, und versuchen Sie, Ihr Problem zu reproduzieren. In der Regel finden Sie die Lösung für das Problem, indem Sie Ihren Code mit dem fertigen Code vergleichen. Einige häufige Fehler und deren Lösung finden Sie unter "Fehler und Problemumgehungen".
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 Sie passen das Datenmodell an, indem Sie Formatierungs-, Validierungs- und Datenbankzuordnungsregeln angeben. Es werden zwei Möglichkeiten zum Anpassen des Datenmodells angezeigt: indem Sie Attribute zu Entitätsklassen hinzufügen und der Datenbankkontextklasse Code hinzufügen.
Wenn Sie dies erledigt haben, bilden die Entitätsklassen ein vollständiges Datenmodell, das in der folgenden Abbildung dargestellt wird:
Anpassen des Datenmodells mithilfe von Attributen
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 StudentID { 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)]
public DateTime EnrollmentDate { get; set; }
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 gebietsschemabezogenen Währungssymbols, E-Mail-Links 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 , wenn es von sich selbst verwendet wird, 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.
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 auch Datenüberprüfungsregeln und Nachrichten mithilfe von Attributen angeben. 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 StudentID { 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 MaxLengthOnNames
Befehl erstellt eine Datei mit dem Namen <"timeStamp>_MaxLengthOnNames.cs". Diese Datei enthält Code, der die Datenbank entsprechend dem aktuellen Datenmodell aktualisiert. Der zeitstempel, der dem Migrationsdateinamen vorangestellt ist, wird von Entity Framework verwendet, um die Migrationen zu ordnen. Nachdem Sie mehrere Migrationen erstellt haben, wenn Sie die Datenbank ablegen oder das Projekt mithilfe von Migrationen bereitstellen, 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. Sobald Sie 50 Zeichen überschreiten, zeigt die clientseitige Überprüfung sofort eine Fehlermeldung an.
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 eine using-Anweisung für System.ComponentModel.DataAnnotations.Schema und das Spaltennamen-Attribut der FirstMidName
Eigenschaft hinzu, wie im folgenden hervorgehobenen Code dargestellt:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace ContosoUniversity.Models
{
public class Student
{
public int StudentID { 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
Doppelklicken Sie im Server-Explorer (Datenbank-Explorer, wenn Sie Express für Web verwenden) auf die Tabelle "Student".
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 versuchen, diese Entitätsklassen zu kompilieren, bevor Sie alle diese Entitätsklassen erstellen, erhalten Sie möglicherweise Compilerfehler.
Erstellen der Instructor-Entität
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 InstructorID { 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; }
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 Lernprogramm zum Implementieren der Vererbung weiter unten in dieser Reihe werden Sie die Vererbung umgestalten, um diese Redundanz zu vermeiden.
Die erforderlichen und anzeigeattribute
Die Attribute für die LastName
Eigenschaft geben an, dass es sich um ein erforderliches Feld handelt, dass die Beschriftung für das Textfeld "Nachname" sein soll (anstelle des Eigenschaftennamens, der "Nachname" ohne Leerzeichen wäre), und dass der Wert nicht länger als 50 Zeichen sein kann.
[Required]
[Display(Name="Last Name")]
[StringLength(50)]
public string LastName { get; set; }
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. Das Attribut "Required " ist für Werttypen wie DateTime, int, double und float nicht erforderlich. Werttypen können keine NULL-Werte zugewiesen werden, daher sind sie inhärent erforderlich. Sie können das Attribut "Required " entfernen und durch einen Minimallängenparameter für das StringLength
Attribut ersetzen:
[Display(Name = "Last Name")]
[StringLength(50, MinimumLength=1)]
public string LastName { get; set; }
Sie können mehrere Attribute in eine Zeile setzen, sodass Sie auch den Kursleiter wie folgt schreiben können:
public class Instructor
{
public int InstructorID { 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; }
public string FullName
{
get { return LastName + ", " + FirstMidName; }
}
public virtual ICollection<Course> Courses { get; set; }
public virtual OfficeAssignment OfficeAssignment { get; set; }
}
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.
public string FullName
{
get { return LastName + ", " + FirstMidName; }
}
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. 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 ICollection<Course> Courses { get; set; }
public virtual OfficeAssignment OfficeAssignment { get; set; }
Erstellen der OfficeAssignment-Entität
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.
Im späteren Lernprogramm wird gezeigt, 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 Entität „Course“
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; }
[Display(Name = "Department")]
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 Abteilungsentität
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)]
public DateTime StartDate { get; set; }
[Display(Name = "Administrator")]
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.
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 Zirkelweitergabe-Löschregeln führen, was zu einer Ausnahme führt, wenn der Initialisierungscode ausgeführt wird. Wenn Sie die
Department.InstructorID
Eigenschaft z. B. nicht als Nullwerte definiert haben, erhalten Sie die folgende Ausnahmemeldung, wenn der Initialisierer ausgeführt wird: "Die referenzielle Beziehung führt zu einem nicht zulässigen zyklischen Verweis." Wenn Ihre Geschäftsregeln eine Eigenschaft als nicht nullwertig benötigenInstructorID
, müssen Sie die folgende Fluent-API verwenden, um die Löschweitergabe für die Beziehung zu deaktivieren:
modelBuilder.Entity().HasRequired(d => d.Administrator).WithMany().WillCascadeOnDelete(false);
Ändern der Schülerentität
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 StudentID { get; set; }
[StringLength(50, MinimumLength = 1)]
public string LastName { get; set; }
[StringLength(50, MinimumLength = 1, 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)]
[Display(Name = "Enrollment Date")]
public DateTime EnrollmentDate { get; set; }
public string FullName
{
get { return LastName + ", " + FirstMidName; }
}
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
}
Die Registrierungsentität
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ätsdiagramm mit angezeigten Beziehungen
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.
Anpassen des Datenmodells durch 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 fließend, da sie häufig durch Zeichenfolgen einer Reihe von Methodenaufrufen in eine einzelne Anweisung verwendet wird.)
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 .
Ausführen eines Seedings für die 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 System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Migrations;
using System.Linq;
using ContosoUniversity.Models;
using ContosoUniversity.DAL;
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").InstructorID },
new Department { Name = "Mathematics", Budget = 100000,
StartDate = DateTime.Parse("2007-09-01"),
InstructorID = instructors.Single( i => i.LastName == "Fakhouri").InstructorID },
new Department { Name = "Engineering", Budget = 350000,
StartDate = DateTime.Parse("2007-09-01"),
InstructorID = instructors.Single( i => i.LastName == "Harui").InstructorID },
new Department { Name = "Economics", Budget = 100000,
StartDate = DateTime.Parse("2007-09-01"),
InstructorID = instructors.Single( i => i.LastName == "Kapoor").InstructorID }
};
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").InstructorID,
Location = "Smith 17" },
new OfficeAssignment {
InstructorID = instructors.Single( i => i.LastName == "Harui").InstructorID,
Location = "Gowan 27" },
new OfficeAssignment {
InstructorID = instructors.Single( i => i.LastName == "Kapoor").InstructorID,
Location = "Thompson 304" },
};
officeAssignments.ForEach(s => context.OfficeAssignments.AddOrUpdate(p => p.Location, 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").StudentID,
CourseID = courses.Single(c => c.Title == "Chemistry" ).CourseID,
Grade = Grade.A
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Alexander").StudentID,
CourseID = courses.Single(c => c.Title == "Microeconomics" ).CourseID,
Grade = Grade.C
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Alexander").StudentID,
CourseID = courses.Single(c => c.Title == "Macroeconomics" ).CourseID,
Grade = Grade.B
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Alonso").StudentID,
CourseID = courses.Single(c => c.Title == "Calculus" ).CourseID,
Grade = Grade.B
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Alonso").StudentID,
CourseID = courses.Single(c => c.Title == "Trigonometry" ).CourseID,
Grade = Grade.B
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Alonso").StudentID,
CourseID = courses.Single(c => c.Title == "Composition" ).CourseID,
Grade = Grade.B
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Anand").StudentID,
CourseID = courses.Single(c => c.Title == "Chemistry" ).CourseID
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Anand").StudentID,
CourseID = courses.Single(c => c.Title == "Microeconomics").CourseID,
Grade = Grade.B
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Barzdukas").StudentID,
CourseID = courses.Single(c => c.Title == "Chemistry").CourseID,
Grade = Grade.B
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Li").StudentID,
CourseID = courses.Single(c => c.Title == "Composition").CourseID,
Grade = Grade.B
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Justice").StudentID,
CourseID = courses.Single(c => c.Title == "Literature").CourseID,
Grade = Grade.B
}
};
foreach (Enrollment e in enrollments)
{
var enrollmentInDataBase = context.Enrollments.Where(
s =>
s.Student.StudentID == 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,
Department = departments.Single( s => s.Name == "Engineering"),
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 und Aktualisieren der Datenbank
Geben Sie im PMC den add-migration
Befehl ein:
PM> add-Migration Chap4
Wenn Sie versuchen, die Datenbank zu diesem Zeitpunkt zu aktualisieren, wird die folgende Fehlermeldung angezeigt:
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.
Bearbeiten Sie den <Zeitstempel>_Chap4.cs datei, und nehmen Sie die folgenden Codeänderungen vor (Sie fügen eine SQL-Anweisung hinzu und ändern eine AddColumn
Anweisung):
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));
AddForeignKey("dbo.Course", "DepartmentID", "dbo.Department", "DepartmentID", cascadeDelete: true);
CreateIndex("dbo.Course", "DepartmentID");
}
public override void Down()
{
(Stellen Sie sicher, dass Sie die vorhandene AddColumn
Zeile auskommentieren oder löschen, wenn Sie die neue Zeile hinzufügen, oder wenn Sie den update-database
Befehl eingeben, wird eine Fehlermeldung angezeigt.)
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 ist jetzt der Vorgang. Der generierte Code fügt der Course
Tabelle einen nicht nullfähigen DepartmentID
Fremdschlüssel hinzu. Wenn 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 haben Sie den Code so geändert, dass die neue Spalte einen Standardwert erhält, und Sie haben eine Stubabteilung mit dem Namen "Temp" erstellt, um als Standardabteilung zu fungieren. Wenn beim Ausführen dieses Codes Zeilen vorhanden Course
sind, beziehen sie sich alle auf die Abteilung "Temp".
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.
Nachdem Sie die Bearbeitung des <Zeitstempels> abgeschlossen haben_Chap4.cs Datei, geben Sie den update-database
Befehl in der PMC-Datei ein, um die Migration auszuführen.
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 entweder die Verbindungszeichenfolge in der Datei "Web.config" ändern oder die Datenbank löschen. Der einfachste Ansatz besteht darin, die Datenbank in der Datei "Web.config " umzubenennen. Ändern Sie z. B. den Datenbanknamen in CU_test, wie in der folgenden Abbildung gezeigt:
<add name="SchoolContext" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=CU_Test;
Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\CU_Test.mdf"
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.
Ö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.
Zusammenfassung
Sie besitzen nun ein komplexeres Datenmodell und eine zugehörige Datenbank. Im folgenden Lernprogramm erfahren Sie mehr über verschiedene Möglichkeiten für den Zugriff auf verwandte Daten.
Links zu anderen Entity Framework-Ressourcen finden Sie in der ASP.NET Datenzugriffs-Inhaltszuordnung.