Entity Framework - Datenanmerkungen
DataAnnotations wird verwendet, um die Klassen zu konfigurieren, die die am häufigsten benötigten Konfigurationen hervorheben. DataAnnotations werden auch von einer Reihe von .NET-Anwendungen verstanden, z. B. ASP.NET MVC, mit denen diese Anwendungen dieselben Annotationen für clientseitige Validierungen nutzen können. DataAnnotation-Attribute überschreiben die Standard-CodeFirst-Konventionen.
System.ComponentModel.DataAnnotations Enthält die folgenden Attribute, die sich auf die Nullfähigkeit oder Größe der Spalte auswirken.
- Key
- Timestamp
- ConcurrencyCheck
- Required
- MinLength
- MaxLength
- StringLength
System.ComponentModel.DataAnnotations.Schema Der Namespace enthält die folgenden Attribute, die sich auf das Schema der Datenbank auswirken.
- Table
- Column
- Index
- ForeignKey
- NotMapped
- InverseProperty
Schlüssel
Entity Framework basiert darauf, dass jede Entität einen Schlüsselwert hat, den sie zum Verfolgen von Entitäten verwendet. Eine der Konventionen, von denen Code First abhängt, ist, wie impliziert wird, welche Eigenschaft der Schlüssel in jeder der Code First-Klassen ist.
Konvention besteht darin, nach einer Eigenschaft mit dem Namen "Id" zu suchen oder nach einer Eigenschaft, die den Klassennamen und "Id" kombiniert, z. B. "StudentId".
Die Eigenschaft wird einer Primärschlüsselspalte in der Datenbank zugeordnet.
Die Klassen für Studenten, Kurse und Einschreibungen folgen dieser Konvention.
Nehmen wir nun an, die Schülerklasse hat den Namen StdntID anstelle von ID verwendet. Wenn Code First keine Eigenschaft findet, die dieser Konvention entspricht, wird eine Ausnahme ausgelöst, da Entity Framework erfordert, dass Sie über eine Schlüsseleigenschaft verfügen müssen. Mit der Schlüsselanmerkung können Sie angeben, welche Eigenschaft als EntityKey verwendet werden soll.
Schauen wir uns den folgenden Code einer Student-Klasse an, die StdntID enthält, aber nicht der Standardkonvention von Code First folgt. Um dies zu handhaben, wird ein Schlüsselattribut hinzugefügt, das es zu einem Primärschlüssel macht.
public class Student {
[Key]
public int StdntID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
Wenn Sie Ihre Anwendung ausführen und in SQL Server Explorer in Ihre Datenbank schauen, sehen Sie, dass der Primärschlüssel jetzt StdntID in der Schülertabelle ist.
Entity Framework unterstützt auch zusammengesetzte Schlüssel. Composite keyssind auch Primärschlüssel, die aus mehr als einer Eigenschaft bestehen. Sie haben beispielsweise eine DrivingLicense-Klasse, deren Primärschlüssel eine Kombination aus LicenseNumber und IssuingCountry ist.
public class DrivingLicense {
[Key, Column(Order = 1)]
public int LicenseNumber { get; set; }
[Key, Column(Order = 2)]
public string IssuingCountry { get; set; }
public DateTime Issued { get; set; }
public DateTime Expires { get; set; }
}
Wenn Sie über zusammengesetzte Schlüssel verfügen, müssen Sie in Entity Framework eine Reihenfolge der Schlüsseleigenschaften definieren. Sie können dies mithilfe der Spaltenanmerkung tun, um eine Reihenfolge anzugeben.
Zeitstempel
Code First behandelt Timestamp-Eigenschaften genauso wie ConcurrencyCheck-Eigenschaften, stellt jedoch auch sicher, dass das vom Code zuerst generierte Datenbankfeld nicht nullwertfähig ist.
Es ist üblicher, Zeilenversions- oder Zeitstempelfelder für die Parallelitätsprüfung zu verwenden.
Anstatt die ConcurrencyCheck-Annotation zu verwenden, können Sie die spezifischere TimeStamp-Annotation verwenden, solange der Typ der Eigenschaft ein Byte-Array ist.
Sie können nur eine Zeitstempeleigenschaft in einer bestimmten Klasse haben.
Schauen wir uns ein einfaches Beispiel an, indem wir der Course-Klasse die TimeStamp-Eigenschaft hinzufügen.
public class Course {
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
[Timestamp]
public byte[] TStamp { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
Wie Sie im obigen Beispiel sehen können, wird das Timestamp-Attribut auf die Byte [] -Eigenschaft der Course-Klasse angewendet. Code First erstellt also eine Zeitstempelspalte TStamp
in der Kurstabelle.
ConcurrencyCheck
Mit der ConcurrencyCheck-Annotation können Sie eine oder mehrere Eigenschaften markieren, die für die Parallelitätsprüfung in der Datenbank verwendet werden sollen, wenn ein Benutzer eine Entität bearbeitet oder löscht. Wenn Sie mit dem EF Designer gearbeitet haben, entspricht dies dem Festlegen des ConcurrencyMode einer Eigenschaft auf "Fest".
Schauen wir uns ein einfaches Beispiel für die Funktionsweise von ConcurrencyCheck an, indem wir es der Title-Eigenschaft in der Course-Klasse hinzufügen.
public class Course {
public int CourseID { get; set; }
[ConcurrencyCheck]
public string Title { get; set; }
public int Credits { get; set; }
[Timestamp, DataType("timestamp")]
public byte[] TimeStamp { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
In der obigen Kursklasse wird das ConcurrencyCheck-Attribut auf die vorhandene Title-Eigenschaft angewendet. Jetzt enthält Code First die Spalte Titel im Aktualisierungsbefehl, um die optimistische Parallelität zu überprüfen, wie im folgenden Code gezeigt.
exec sp_executesql N'UPDATE [dbo].[Courses]
SET [Title] = @0
WHERE (([CourseID] = @1) AND ([Title] = @2))
',N'@0 nvarchar(max) ,@1 int,@2 nvarchar(max) ',@0=N'Maths',@1=1,@2=N'Calculus'
go
Erforderliche Anmerkung
Die Annotation Erforderlich teilt EF mit, dass eine bestimmte Eigenschaft erforderlich ist. Werfen wir einen Blick auf die folgende Student-Klasse, in der der FirstMidName-Eigenschaft die erforderliche ID hinzugefügt wird. Das erforderliche Attribut zwingt EF, sicherzustellen, dass die Eigenschaft Daten enthält.
public class Student {
[Key]
public int StdntID { get; set; }
[Required]
public string LastName { get; set; }
[Required]
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
Wie im obigen Beispiel gezeigt, wird das Attribut Erforderlich auf Vorname und Nachname angewendet. Code First erstellt also in der Tabelle "Students" die Spalten NOT NULL FirstMidName und LastName, wie in der folgenden Abbildung dargestellt.
Maximale Länge
Mit dem MaxLength-Attribut können Sie zusätzliche Eigenschaftsüberprüfungen angeben. Es kann auf eine String- oder Array-Typ-Eigenschaft einer Domänenklasse angewendet werden. EF Code First legt die Größe einer Spalte fest, wie im MaxLength-Attribut angegeben.
Werfen wir einen Blick auf die folgende Kursklasse, in der das Attribut MaxLength (24) auf die Title-Eigenschaft angewendet wird.
public class Course {
public int CourseID { get; set; }
[ConcurrencyCheck]
[MaxLength(24)]
public string Title { get; set; }
public int Credits { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
Wenn Sie die obige Anwendung ausführen, erstellt Code First einen Titel in der Spalte nvarchar (24) in der CourseId-Tabelle, wie in der folgenden Abbildung dargestellt.
Wenn der Benutzer den Titel festlegt, der mehr als 24 Zeichen enthält, löst EF EntityValidationError aus.
Minimale Länge
Mit dem MinLength-Attribut können Sie ebenso wie mit MaxLength zusätzliche Eigenschaftsüberprüfungen angeben. Das MinLength-Attribut kann auch mit dem MaxLength-Attribut verwendet werden, wie im folgenden Code gezeigt.
public class Course {
public int CourseID { get; set; }
[ConcurrencyCheck]
[MaxLength(24) , MinLength(5)]
public string Title { get; set; }
public int Credits { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
EF löst EntityValidationError aus, wenn Sie einen Wert für die Title-Eigenschaft festlegen, der kleiner als die angegebene Länge im MinLength-Attribut oder größer als die angegebene Länge im MaxLength-Attribut ist.
String-Länge
Mit StringLength können Sie auch zusätzliche Eigenschaftsüberprüfungen wie MaxLength angeben. Der einzige Unterschied besteht darin, dass das StringLength-Attribut nur auf eine Zeichenfolgentyp-Eigenschaft von Domänenklassen angewendet werden kann.
public class Course {
public int CourseID { get; set; }
[StringLength (24)]
public string Title { get; set; }
public int Credits { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
Entity Framework überprüft auch den Wert einer Eigenschaft für das StringLength-Attribut. Wenn der Benutzer den Titel festlegt, der mehr als 24 Zeichen enthält, löst EF EntityValidationError aus.
Tabelle
Standardcode Die erste Konvention erstellt einen Tabellennamen ähnlich dem Klassennamen. Wenn Sie Code First die Datenbank erstellen lassen und auch den Namen der Tabellen ändern möchten, die erstellt werden. Dann -
Sie können Code First mit einer vorhandenen Datenbank verwenden. Es ist jedoch nicht immer so, dass die Namen der Klassen mit den Namen der Tabellen in Ihrer Datenbank übereinstimmen.
Das Tabellenattribut überschreibt diese Standardkonvention.
EF Code First erstellt eine Tabelle mit einem angegebenen Namen im Tabellenattribut für eine bestimmte Domänenklasse.
Schauen wir uns das folgende Beispiel an, in dem die Klasse Student heißt, und Code First geht davon aus, dass dies einer Tabelle mit dem Namen Students zugeordnet wird. Ist dies nicht der Fall, können Sie den Namen der Tabelle mit dem Tabellenattribut angeben, wie im folgenden Code gezeigt.
[Table("StudentsInfo")]
public class Student {
[Key]
public int StdntID { get; set; }
[Required]
public string LastName { get; set; }
[Required]
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
Jetzt können Sie sehen, dass das Tabellenattribut die Tabelle als StudentsInfo angibt. Wenn die Tabelle generiert wird, wird der Tabellenname StudentsInfo angezeigt (siehe folgende Abbildung).
Sie können nicht nur den Tabellennamen angeben, sondern auch ein Schema für die Tabelle mithilfe des Tabellenattributs angeben, wie im folgenden Code gezeigt.
[Table("StudentsInfo", Schema = "Admin")]
public class Student {
[Key]
public int StdntID { get; set; }
[Required]
public string LastName { get; set; }
[Required]
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
Im obigen Beispiel sehen Sie, dass die Tabelle mit dem Administratorschema angegeben ist. Jetzt erstellt Code First die StudentsInfo-Tabelle im Admin-Schema, wie in der folgenden Abbildung dargestellt.
Säule
Es ist auch dasselbe wie das Tabellenattribut, aber das Tabellenattribut überschreibt das Tabellenverhalten, während das Spaltenattribut das Spaltenverhalten überschreibt. Standardcode Die erste Konvention erstellt einen Spaltennamen ähnlich dem Eigenschaftsnamen. Wenn Sie Code First die Datenbank erstellen lassen und auch den Namen der Spalten in Ihren Tabellen ändern möchten. Dann -
Das Spaltenattribut überschreibt die Standardkonvention.
EF Code First erstellt eine Spalte mit einem angegebenen Namen im Column-Attribut für eine bestimmte Eigenschaft.
Schauen wir uns das folgende Beispiel an, in dem die Eigenschaft FirstMidName heißt. Gemäß der Konvention geht Code First davon aus, dass dies einer Spalte mit dem Namen FirstMidName zugeordnet wird.
Ist dies nicht der Fall, können Sie den Namen der Spalte mit dem Column-Attribut angeben, wie im folgenden Code gezeigt.
public class Student {
public int ID { get; set; }
public string LastName { get; set; }
[Column("FirstName")]
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
Sie können sehen, dass das Spaltenattribut die Spalte als Vorname angibt. Wenn die Tabelle generiert wird, wird der Spaltenname FirstName angezeigt (siehe folgende Abbildung).
Index
Das Index-Attribut wurde in Entity Framework 6.1 eingeführt. Wenn Sie eine frühere Version verwenden, gelten die Informationen in diesem Abschnitt nicht.
Mit dem IndexAttribute können Sie einen Index für eine oder mehrere Spalten erstellen.
Durch Hinzufügen des Attributs zu einer oder mehreren Eigenschaften erstellt EF beim Erstellen der Datenbank den entsprechenden Index in der Datenbank.
Indizes machen das Abrufen von Daten in den meisten Fällen schneller und effizienter. Das Überladen einer Tabelle oder Ansicht mit Indizes kann jedoch die Leistung anderer Vorgänge wie Einfügungen oder Aktualisierungen unangenehm beeinträchtigen.
Die Indizierung ist die neue Funktion in Entity Framework, mit der Sie die Leistung Ihrer Code First-Anwendung verbessern können, indem Sie die Zeit reduzieren, die zum Abfragen von Daten aus der Datenbank erforderlich ist.
Sie können Ihrer Datenbank mithilfe des Indexattributs Indizes hinzufügen und die Standardeinstellungen für Eindeutig und Clustered überschreiben, um den für Ihr Szenario am besten geeigneten Index zu erhalten.
Standardmäßig heißt der Index IX_ <Eigenschaftsname>
Schauen wir uns den folgenden Code an, in dem das Indexattribut in der Kursklasse für Credits hinzugefügt wird.
public class Course {
public int CourseID { get; set; }
public string Title { get; set; }
[Index]
public int Credits { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
Sie können sehen, dass das Indexattribut auf die Credits-Eigenschaft angewendet wird. Wenn die Tabelle generiert wird, wird IX_Credits in den Indizes angezeigt.
Standardmäßig sind Indizes nicht eindeutig, Sie können jedoch die verwenden IsUniquebenannter Parameter, um anzugeben, dass ein Index eindeutig sein soll. Im folgenden Beispiel wird ein eindeutiger Index eingeführt, wie im folgenden Code gezeigt.
public class Course {
public int CourseID { get; set; }
[Index(IsUnique = true)]
public string Title { get; set; }
[Index]
public int Credits { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
Unbekannter Schlüssel
Die Code First-Konvention kümmert sich um die häufigsten Beziehungen in Ihrem Modell, aber es gibt einige Fälle, in denen Hilfe benötigt wird. Durch Ändern des Namens der Schlüsseleigenschaft in der Student-Klasse wurde beispielsweise ein Problem mit der Beziehung zur Enrollment-Klasse verursacht.
public class Enrollment {
public int EnrollmentID { get; set; }
public int CourseID { get; set; }
public int StudentID { get; set; }
public Grade? Grade { get; set; }
public virtual Course Course { get; set; }
public virtual Student Student { get; set; }
}
public class Student {
[Key]
public int StdntID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
Beim Generieren der Datenbank erkennt Code First die StudentID-Eigenschaft in der Enrollment-Klasse und erkennt sie anhand der Konvention, dass sie mit einem Klassennamen plus „ID“ übereinstimmt, als Fremdschlüssel für die Student-Klasse. Es gibt jedoch keine StudentID-Eigenschaft in der Student-Klasse, aber die StdntID-Eigenschaft ist die Student-Klasse.
Die Lösung hierfür besteht darin, eine Navigationseigenschaft in der Registrierung zu erstellen und die ForeignKey DataAnnotation zu verwenden, um Code zu helfen. Verstehen Sie zunächst, wie die Beziehung zwischen den beiden Klassen aufgebaut wird, wie im folgenden Code gezeigt.
public class Enrollment {
public int EnrollmentID { get; set; }
public int CourseID { get; set; }
public int StudentID { get; set; }
public Grade? Grade { get; set; }
public virtual Course Course { get; set; }
[ForeignKey("StudentID")]
public virtual Student Student { get; set; }
}
Sie können jetzt sehen, dass das ForeignKey-Attribut auf die Navigationseigenschaft angewendet wird.
NotMapped
Standardmäßig werden in der Datenbank alle Eigenschaften von Code First dargestellt, die von einem unterstützten Datentyp sind und Getter und Setter enthalten. Dies ist jedoch in Ihren Anwendungen nicht immer der Fall. Das NotMapped-Attribut überschreibt diese Standardkonvention. Beispielsweise haben Sie möglicherweise eine Eigenschaft in der Student-Klasse, z. B. FatherName, die jedoch nicht gespeichert werden muss. Sie können das NotMapped-Attribut auf eine FatherName-Eigenschaft anwenden, für die Sie keine Spalte in der Datenbank erstellen möchten, wie im folgenden Code gezeigt.
public class Student {
[Key]
public int StdntID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
[NotMapped]
public int FatherName { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
Sie können sehen, dass das NotMapped-Attribut auf die FatherName-Eigenschaft angewendet wird. Wenn die Tabelle generiert wird, sehen Sie, dass die FatherName-Spalte nicht in einer Datenbank erstellt wird, sondern in der Student-Klasse vorhanden ist.
Code First erstellt keine Spalte für eine Eigenschaft, die weder Getter noch Setter enthält, wie im folgenden Beispiel für die Eigenschaften Adresse und Alter der Schülerklasse gezeigt.
InverseProperty
InverseProperty wird verwendet, wenn Sie mehrere Beziehungen zwischen Klassen haben. In der Einschreibeklasse möchten Sie möglicherweise nachverfolgen, wer einen aktuellen und einen vorherigen Kurs eingeschrieben hat. Fügen wir zwei Navigationseigenschaften für die Registrierungsklasse hinzu.
public class Enrollment {
public int EnrollmentID { get; set; }
public int CourseID { get; set; }
public int StudentID { get; set; }
public Grade? Grade { get; set; }
public virtual Course CurrCourse { get; set; }
public virtual Course PrevCourse { get; set; }
public virtual Student Student { get; set; }
}
In ähnlicher Weise müssen Sie auch die Kursklasse hinzufügen, auf die diese Eigenschaften verweisen. Die Kursklasse verfügt über Navigationseigenschaften zurück zur Registrierungsklasse, die alle aktuellen und vorherigen Anmeldungen enthält.
public class Course {
public int CourseID { get; set; }
public string Title { get; set; }
[Index]
public int Credits { get; set; }
public virtual ICollection<Enrollment> CurrEnrollments { get; set; }
public virtual ICollection<Enrollment> PrevEnrollments { get; set; }
}
Code Erstellt zuerst die Fremdschlüsselspalte {Klassenname} _ {Primärschlüssel}, wenn die Fremdschlüsseleigenschaft nicht in einer bestimmten Klasse enthalten ist, wie in den obigen Klassen gezeigt. Wenn die Datenbank generiert wird, werden die folgenden Fremdschlüssel angezeigt.
Wie Sie sehen, kann Code first die Eigenschaften in den beiden Klassen nicht alleine abgleichen. Die Datenbanktabelle für Registrierungen sollte einen Fremdschlüssel für den CurrCourse und einen für den PrevCourse enthalten, aber Code First erstellt vier Fremdschlüsseleigenschaften, d. H.
- CurrCourse _CourseID
- PrevCourse _CourseID
- Course_CourseID und
- Course_CourseID1
Um diese Probleme zu beheben, können Sie die Annotation InverseProperty verwenden, um die Ausrichtung der Eigenschaften festzulegen.
public class Course {
public int CourseID { get; set; }
public string Title { get; set; }
[Index]
public int Credits { get; set; }
[InverseProperty("CurrCourse")]
public virtual ICollection<Enrollment> CurrEnrollments { get; set; }
[InverseProperty("PrevCourse")]
public virtual ICollection<Enrollment> PrevEnrollments { get; set; }
}
Wie Sie sehen können, wird das InverseProperty-Attribut in der obigen Kursklasse angewendet, indem angegeben wird, zu welcher Referenzeigenschaft der Registrierungsklasse sie gehört. Jetzt generiert Code First eine Datenbank und erstellt nur zwei Fremdschlüsselspalten in der Registrierungstabelle, wie in der folgenden Abbildung dargestellt.
Wir empfehlen, dass Sie das obige Beispiel zum besseren Verständnis Schritt für Schritt ausführen.