Tutoriel : Créer un modèle de données plus complexe pour une application MVC ASP.NET
Dans les didacticiels précédents, vous avez travaillé avec un modèle de données simple composé de trois entités. Dans ce tutoriel, vous ajoutez d’autres entités et relations et vous personnalisez le modèle de données en spécifiant des règles de mise en forme, de validation et de mappage de base de données. Cet article montre deux façons de personnaliser le modèle de données : en ajoutant des attributs aux classes d’entité et en ajoutant du code à la classe de contexte de base de données.
Lorsque vous aurez terminé, les classes d’entité composeront le modèle de données complet indiqué dans l’illustration suivante :
Dans ce tutoriel, vous allez :
- Personnaliser le modèle de données
- Mettre à jour l’entité Student
- Créer une entité Instructor
- Créer une entité OfficeAssignment
- Modifier l’entité Course
- Créer l’entité Department
- Modifier l’entité Enrollment
- Ajouter du code au contexte de base de données
- Remplir la base de données avec des données de test
- Ajouter une migration
- Mettre à jour la base de données
Prérequis
Personnaliser le modèle de données
Dans cette section, vous allez apprendre à personnaliser le modèle de données en utilisant des attributs qui spécifient des règles de mise en forme, de validation et de mappage de base de données. Ensuite, dans plusieurs des sections suivantes, vous allez créer le modèle de données complet School
en ajoutant des attributs aux classes que vous avez déjà créées et en créant de nouvelles classes pour les types d’entités restants dans le modèle.
Attribut DataType
Pour les dates d’inscription des étudiants, toutes les pages web affichent l’heure avec la date, alors que seule la date vous intéresse dans ce champ. Vous pouvez avoir recours aux attributs d’annotation de données pour apporter une modification au code, permettant de corriger le format d’affichage dans chaque vue qui affiche ces données. Pour voir un exemple de la procédure à suivre, vous allez ajouter un attribut à la propriété EnrollmentDate
dans la classe Student
.
Dans Models\Student.cs, ajoutez une using
instruction pour l’espace System.ComponentModel.DataAnnotations
de noms et ajoutez DataType
et DisplayFormat
attributs à la EnrollmentDate
propriété, comme illustré dans l’exemple suivant :
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; }
}
}
L’attribut DataType est utilisé pour spécifier un type de données plus spécifique que le type intrinsèque de base de données. Dans le cas présent, nous voulons uniquement effectuer le suivi de la date, pas de la date et de l’heure. L’énumération DataType fournit de nombreux types de données, tels que Date, Heure, PhoneNumber, Currency, EmailAddress et bien plus encore. L’attribut DataType
peut également permettre à l’application de fournir automatiquement des fonctionnalités propres au type. Par exemple, un mailto:
lien peut être créé pour DataType.EmailAddress, et un sélecteur de date peut être fourni pour DataType.Date dans les navigateurs qui prennent en charge HTML5. Les attributs DataType émettent des attributs HTML 5 data - (tiret de données prononcés) que les navigateurs HTML 5 peuvent comprendre. Les attributs DataType ne fournissent aucune validation.
DataType.Date
ne spécifie pas le format de la date qui s’affiche. Par défaut, le champ de données s’affiche selon les formats par défaut basés sur CultureInfo du serveur.
L’attribut DisplayFormat
est utilisé pour spécifier explicitement le format de date :
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
Le ApplyFormatInEditMode
paramètre spécifie que la mise en forme spécifiée doit également être appliquée lorsque la valeur est affichée dans une zone de texte pour modification. (Vous ne souhaiterez peut-être pas que pour certains champs , par exemple, pour les valeurs monétaires, vous ne souhaiterez peut-être pas le symbole monétaire dans la zone de texte à modifier.)
Vous pouvez utiliser l’attribut DisplayFormat lui-même, mais il est généralement judicieux d’utiliser également l’attribut DataType . L’attribut DataType
transmet la sémantique des données par opposition à la façon de l’afficher sur un écran et fournit les avantages suivants que vous n’obtenez DisplayFormat
pas :
- Le navigateur peut activer des fonctionnalités HTML5 (par exemple pour afficher un contrôle de calendrier, le symbole monétaire correspondant aux paramètres régionaux, des liens de messagerie, une certaine validation des entrées côté client, etc.).
- Par défaut, le navigateur affiche les données au format approprié en fonction de vos paramètres régionaux.
- L’attribut DataType peut permettre à MVC de choisir le modèle de champ approprié pour afficher les données (DisplayFormat utilise le modèle de chaîne). Pour plus d’informations, consultez les modèles ASP.NET MVC 2 de Brad Wilson. (Bien que écrit pour MVC 2, cet article s’applique toujours à la version actuelle de ASP.NET MVC.)
Si vous utilisez l’attribut DataType
avec un champ de date, vous devez également spécifier l’attribut DisplayFormat
pour vous assurer que le champ s’affiche correctement dans les navigateurs Chrome. Pour plus d’informations, consultez ce thread StackOverflow.
Pour plus d’informations sur la gestion d’autres formats de date dans MVC, accédez à L’introduction de MVC 5 : Examen des méthodes d’édition et de l’affichage Edit et recherche dans la page pour « internationalisation ».
Réexécutez la page Index étudiant et notez que les heures ne sont plus affichées pour les dates d’inscription. Il en va de même pour toute vue qui utilise le Student
modèle.
The StringLengthAttribute
Vous pouvez également spécifier les règles de validation de données et les messages d’erreur de validation à l’aide d’attributs. L’attribut StringLength définit la longueur maximale dans la base de données et fournit la validation côté client et côté serveur pour ASP.NET MVC. Vous pouvez également spécifier la longueur de chaîne minimale dans cet attribut, mais la valeur minimale n’a aucun impact sur le schéma de base de données.
Supposons que vous voulez garantir que les utilisateurs n’entrent pas plus de 50 caractères pour un nom. Pour ajouter cette limitation, ajoutez des attributs StringLength aux propriétés et FirstMidName
aux LastName
propriétés, comme illustré dans l’exemple suivant :
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; }
}
}
L’attribut StringLength n’empêche pas un utilisateur d’entrer un espace blanc pour un nom. Vous pouvez utiliser l’attribut RegularExpression pour appliquer des restrictions à l’entrée. Par exemple, le code suivant nécessite que le premier caractère soit majuscule et que les caractères restants soient alphabétiques :
[RegularExpression(@"^[A-Z]+[a-zA-Z""'\s-]*$")]
L’attribut MaxLength fournit des fonctionnalités similaires à l’attribut StringLength , mais ne fournit pas de validation côté client.
Exécutez l’application et cliquez sur l’onglet Étudiants . Vous obtenez l’erreur suivante :
Le modèle qui sauvegarde le contexte « SchoolContext » a changé depuis la création de la base de données. Envisagez d’utiliser Migrations Code First pour mettre à jour la base de données (https://go.microsoft.com/fwlink/?LinkId=238269).
Le modèle de base de données a changé d’une manière qui nécessite une modification dans le schéma de base de données et Entity Framework a détecté cela. Vous allez utiliser des migrations pour mettre à jour le schéma sans perdre les données que vous avez ajoutées à la base de données à l’aide de l’interface utilisateur. Si vous avez modifié les données créées par la Seed
méthode, celles-ci seront renvoyées à son état d’origine en raison de la méthode AddOrUpdate que vous utilisez dans la Seed
méthode. (AddOrUpdate équivaut à une opération « upsert » à partir de la terminologie de la base de données.)
Dans la console du Gestionnaire de package, entrez les commandes suivantes :
add-migration MaxLengthOnNames
update-database
La add-migration
commande crée un fichier nommé <timeStamp>_MaxLengthOnNames.cs. Ce fichier contient du code dans la méthode Up
qui met à jour la base de données pour qu’elle corresponde au modèle de données actuel. La commande update-database
a exécuté ce code.
L’horodatage ajouté au nom du fichier de migrations est utilisé par Entity Framework pour classer les migrations. Vous pouvez créer plusieurs migrations avant d’exécuter la update-database
commande, puis toutes les migrations sont appliquées dans l’ordre dans lequel elles ont été créées.
Exécutez la page Créer et entrez un nom de plus de 50 caractères. Lorsque vous cliquez sur Créer, la validation côté client affiche un message d’erreur : le champ LastName doit être une chaîne dont la longueur maximale est de 50.
Attribut de colonne
Vous pouvez également utiliser des attributs pour contrôler la façon dont les classes et les propriétés sont mappées à la base de données. Supposons que vous aviez utilisé le nom FirstMidName
pour le champ de prénom, car le champ peut également contenir un deuxième prénom. Mais vous souhaitez que la colonne de base de données soit nommée FirstName
, car les utilisateurs qui écriront des requêtes ad-hoc par rapport à la base de données sont habitués à ce nom. Pour effectuer ce mappage, vous pouvez utiliser l’attribut Column
.
L’attribut Column
spécifie que lorsque la base de données sera créée, la colonne de la table Student
qui est mappée sur la propriété FirstMidName
sera nommée FirstName
. En d’autres termes, lorsque votre code fait référence à Student.FirstMidName
, les données proviennent de la colonne FirstName
de la table Student
ou y sont mises à jour. Si vous ne spécifiez pas de noms de colonnes, ils ont le même nom que le nom de la propriété.
Dans le fichier Student.cs, ajoutez une using
instruction pour System.ComponentModel.DataAnnotations.Schema et ajoutez l’attribut de nom de colonne à la FirstMidName
propriété, comme indiqué dans le code mis en surbrillance suivant :
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; }
}
}
L’ajout de l’attribut Column modifie le modèle de sauvegarde de SchoolContext. Il ne correspond donc pas à la base de données. Entrez les commandes suivantes dans le PMC pour créer une autre migration :
add-migration ColumnFirstName
update-database
Dans l’Explorateur de serveurs, ouvrez le concepteur de tables Student en double-cliquant sur la table Student .
L’image suivante montre le nom de colonne d’origine tel qu’il était avant d’appliquer les deux premières migrations. En plus du nom de FirstMidName
colonne qui passe de , FirstName
les deux colonnes de nom ont changé de MAX
longueur à 50 caractères.
Vous pouvez également apporter des modifications de mappage de base de données à l’aide de l’API Fluent, comme vous le verrez plus loin dans ce tutoriel.
Remarque
Si vous essayez de compiler avant d’avoir fini de créer toutes les classes d’entité dans les sections suivantes, vous pouvez obtenir des erreurs de compilation.
Mettre à jour l’entité Student
Dans Models\Student.cs, remplacez le code que vous avez ajouté précédemment par le code suivant. Les modifications sont mises en surbrillance.
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; }
}
}
Attribut obligatoire
L’attribut Obligatoire rend les champs obligatoires des propriétés de nom. Il Required attribute
n’est pas nécessaire pour les types valeur tels que DateTime, int, double et float. Les types valeur ne peuvent pas être attribués à une valeur Null. Ils sont donc traités par nature comme des champs obligatoires.
L'attribut Required
doit être utilisé avec MinimumLength
pour appliquer MinimumLength
.
[Display(Name = "Last Name")]
[Required]
[StringLength(50, MinimumLength=2)]
public string LastName { get; set; }
MinimumLength
et Required
autorisent un espace blanc pour satisfaire la validation. Utilisez l’attribut RegularExpression
pour contrôler entièrement la chaîne.
Attribut d’affichage
L’attribut Display
spécifie que la légende pour les zones de texte doit être « First Name », « Last Name », « Full Name » et « Enrollment Date », au lieu du nom de propriété dans chaque instance (qui n’a pas d’espace pour séparer les mots).
FullName Calculated, propriété
FullName
est une propriété calculée qui retourne une valeur créée par concaténation de deux autres propriétés. Par conséquent, il n’a qu’un get
accesseur, et aucune colonne ne FullName
sera générée dans la base de données.
Créer une entité Instructor
Créez models\Instructor.cs, en remplaçant le code de modèle par le code suivant :
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; }
}
}
Notez que plusieurs propriétés sont identiques dans les entités Student
et Instructor
. Dans le didacticiel Implémentation de l’héritage plus loin dans cette série, vous allez refactoriser ce code pour éliminer la redondance.
Vous pouvez placer plusieurs attributs sur une ligne, de sorte que vous pouvez également écrire la classe d’instructeur comme suit :
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; }
}
Propriétés de navigation courses et OfficeAssignment
Les propriétés Courses
et OfficeAssignment
sont des propriétés de navigation. Comme expliqué précédemment, ils sont généralement définis comme virtuels afin qu’ils puissent tirer parti d’une fonctionnalité Entity Framework appelée chargement différé. En outre, si une propriété de navigation peut contenir plusieurs entités, son type doit implémenter l’interface T> ICollection<. Par exemple , IList<T> qualifie, mais pas IEnumerable<T> , car IEnumerable<T>
n’implémente pas Add.
Un instructeur peut enseigner n’importe quel nombre de cours. Courses
Il est donc défini comme une collection d’entités Course
.
public virtual ICollection<Course> Courses { get; set; }
Nos règles d’entreprise indiquent qu’un instructeur ne peut avoir qu’un seul bureau. OfficeAssignment
Il est donc défini comme une entité unique OfficeAssignment
(qui peut être null
si aucun bureau n’est affecté).
public virtual OfficeAssignment OfficeAssignment { get; set; }
Créer une entité OfficeAssignment
Créez des modèles\OfficeAssignment.cs avec le code suivant :
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; }
}
}
Générez le projet, qui enregistre vos modifications et vérifie que vous n’avez pas effectué d’erreurs de copie et de collage que le compilateur peut intercepter.
Attribut clé
Il existe une relation un-à-zéro-ou-un entre les entités Instructor
et OfficeAssignment
. Une affectation de bureau existe uniquement par rapport à l’instructeur auquel elle est affectée. Ainsi, sa clé primaire est également sa clé étrangère pour l’entité Instructor
. Mais Entity Framework ne peut pas reconnaître InstructorID
automatiquement comme clé primaire de cette entité, car son nom ne suit pas la ID
convention d’affectation de noms de nommage ou nom_classeID
. Par conséquent, l’attribut Key
est utilisé pour l’identifier comme clé :
[Key]
[ForeignKey("Instructor")]
public int InstructorID { get; set; }
Vous pouvez également utiliser l’attribut Key
si l’entité possède sa propre clé primaire, mais que vous souhaitez nommer la propriété autre chose que classnameID
ou ID
. Par défaut, EF traite la clé comme non générée par la base de données, car la colonne est destinée à une relation d’identification.
Attribut ForeignKey
Lorsqu’il existe une relation un-à-zéro-ou-un ou une relation un-à-un entre deux entités (par exemple entre OfficeAssignment
et Instructor
), EF ne peut pas déterminer la fin de la relation est le principal et la fin qui dépend. Les relations un-à-un ont une propriété de navigation de référence dans chaque classe vers l’autre classe. L’attribut ForeignKey peut être appliqué à la classe dépendante pour établir la relation. Si vous omettez l’attribut ForeignKey, vous obtenez l’erreur suivante lorsque vous essayez de créer la migration :
Impossible de déterminer la fin principale d’une association entre les types « ContosoUniversity.Models.OfficeAssignment » et « ContosoUniversity.Models.Instructor ». La fin principale de cette association doit être configurée explicitement à l’aide de l’API Fluent de relation ou des annotations de données.
Plus loin dans le tutoriel, vous verrez comment configurer cette relation avec l’API Fluent.
Propriété de navigation de l’instructeur
L’entité Instructor
a une propriété de navigation nullable OfficeAssignment
(car un instructeur n’a peut-être pas d’affectation de bureau) et l’entité OfficeAssignment
a une propriété de navigation non nullable Instructor
(car une attribution de bureau ne peut pas exister sans instructeur - InstructorID
n’est pas nullable). Lorsqu’une Instructor
entité a une entité associée OfficeAssignment
, chaque entité aura une référence à l’autre dans sa propriété de navigation.
Vous pouvez placer un [Required]
attribut sur la propriété de navigation Instructor pour spécifier qu’il doit y avoir un instructeur associé, mais vous n’avez pas à le faire, car la clé étrangère InstructorID (qui est également la clé de cette table) n’est pas nullable.
Modifier l’entité Course
Dans Models\Course.cs, remplacez le code que vous avez ajouté précédemment par le code suivant :
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; }
}
}
L’entité de cours a une propriété DepartmentID
de clé étrangère qui pointe vers l’entité associée Department
et a une Department
propriété de navigation. Entity Framework ne vous demande pas d’ajouter une propriété de clé étrangère à votre modèle de données lorsque vous avez une propriété de navigation pour une entité associée. EF crée automatiquement des clés étrangères dans la base de données où qu’elles soient nécessaires. Mais le fait d’avoir la clé étrangère dans le modèle de données peut rendre les mises à jour plus simples et plus efficaces. Par exemple, lorsque vous récupérez une entité de cours à modifier, l’entité Department
est null si vous ne la chargez pas. Par conséquent, lorsque vous mettez à jour l’entité de cours, vous devrez d’abord extraire l’entité Department
. Quand la propriété de clé étrangère DepartmentID
est incluse dans le modèle de données, vous n’avez pas besoin de récupérer l’entité Department
avant d’effectuer la mise à jour.
Attribut DatabaseGenerated
L’attribut DatabaseGenerated avec le paramètre None sur la CourseID
propriété spécifie que les valeurs de clé primaire sont fournies par l’utilisateur plutôt que générées par la base de données.
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Display(Name = "Number")]
public int CourseID { get; set; }
Par défaut, Entity Framework part du principe que les valeurs de clé primaire sont générées par la base de données. C’est ce que vous souhaitez dans la plupart des scénarios. Toutefois, pour les entités Course
, vous allez utiliser un numéro de cours spécifié par l’utilisateur, par exemple la série 1 000 pour un service, la série 2 000 pour un autre service, etc.
Propriétés de la clé étrangère et de la navigation
Les propriétés de clé étrangère et les propriétés de navigation dans l’entité Course
reflètent les relations suivantes :
Un cours est affecté à un seul département, donc il existe une clé étrangère
DepartmentID
et une propriété de navigationDepartment
pour les raisons mentionnées ci-dessus.public int DepartmentID { get; set; } public virtual Department Department { get; set; }
Un cours pouvant avoir un nombre quelconque d’étudiants inscrits, la propriété de navigation
Enrollments
est une collection :public virtual ICollection<Enrollment> Enrollments { get; set; }
Un cours pouvant être animé par plusieurs formateurs, la propriété de navigation
Instructors
est une collection :public virtual ICollection<Instructor> Instructors { get; set; }
Créer l’entité Department
Créez des modèles\Department.cs avec le code suivant :
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; }
}
}
Attribut de colonne
Précédemment, vous avez utilisé l’attribut Column pour modifier le mappage de nom de colonne. Dans le code de l’entitéDepartment
, l’attribut Column
est utilisé pour modifier le mappage de type de données SQL afin que la colonne soit définie à l’aide du type d’argent SQL Server dans la base de données :
[Column(TypeName="money")]
public decimal Budget { get; set; }
Le mappage de colonnes n’est généralement pas obligatoire, car Entity Framework choisit généralement le type de données SQL Server approprié en fonction du type CLR que vous définissez pour la propriété. Le type CLR decimal
est mappé à un type SQL Server decimal
. Mais dans ce cas, vous savez que la colonne contiendra des montants monétaires et que le type de données monétaires est plus approprié pour cela. Pour plus d’informations sur les types de données CLR et leur correspondance avec les types de données SQL Server, consultez SqlClient pour Entity FrameworkTypes.
Propriétés de la clé étrangère et de la navigation
Les propriétés de clé étrangère et de navigation reflètent les relations suivantes :
Un département peut ou non avoir un administrateur, et un administrateur est toujours un formateur. Par conséquent, la
InstructorID
propriété est incluse en tant que clé étrangère à l’entitéInstructor
, et un point d’interrogation est ajouté après la désignation deint
type pour marquer la propriété comme nullable. La propriété de navigation est nomméeAdministrator
mais contient uneInstructor
entité :public int? InstructorID { get; set; } public virtual Instructor Administrator { get; set; }
Un service peut avoir de nombreux cours, il existe donc une
Courses
propriété de navigation :public virtual ICollection<Course> Courses { get; set; }
Remarque
Par convention, Entity Framework permet la suppression en cascade pour les clés étrangères non nullables et pour les relations plusieurs à plusieurs. Cela peut entraîner des règles de suppression en cascade circulaires, qui provoqueront une exception lorsque vous essaierez d’ajouter une migration. Par exemple, si vous n’avez pas défini la
Department.InstructorID
propriété comme nullable, vous obtenez le message d’exception suivant : « La relation référentielle entraîne une référence cyclique qui n’est pas autorisée ». Si vos règles d’entreprise exigeaientInstructorID
que la propriété ne soit pas nullable, vous devez utiliser l’instruction API Fluent suivante pour désactiver la suppression en cascade sur la relation :
modelBuilder.Entity().HasRequired(d => d.Administrator).WithMany().WillCascadeOnDelete(false);
Modifier l’entité Enrollment
Dans Models\Enrollment.cs, remplacez le code que vous avez ajouté précédemment par le code suivant.
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; }
}
}
Propriétés de la clé étrangère et de la navigation
Les propriétés de clé étrangère et de navigation reflètent les relations suivantes :
Un enregistrement d’inscription est utilisé pour un cours unique, si bien qu’il existe une propriété de clé étrangère
CourseID
et une propriété de navigationCourse
:public int CourseID { get; set; } public virtual Course Course { get; set; }
Un enregistrement d’inscription est utilisé pour un étudiant unique, si bien qu’il existe une propriété de clé étrangère
StudentID
et une propriété de navigationStudent
:public int StudentID { get; set; } public virtual Student Student { get; set; }
Relations plusieurs-à-plusieurs
Il existe une relation plusieurs-à-plusieurs entre les entités Student
et Course
. L’entité Enrollment
fonctionne comme une table de jointure plusieurs-à-plusieurs avec une charge utile dans la base de données. Cela signifie que la Enrollment
table contient des données supplémentaires en plus des clés étrangères pour les tables jointes (dans ce cas, une clé primaire et une Grade
propriété).
L’illustration suivante montre à quoi ressemblent ces relations dans un diagramme d’entité. (Ce diagramme a été généré à l’aide de la Entity Framework Power Tools ; la création du diagramme ne fait pas partie du didacticiel, elle est simplement utilisée ici comme illustration.)
Chaque ligne de relation comporte un 1 à une extrémité et un astérisque (*) à l’autre, ce qui indique une relation un-à-plusieurs.
Si la table Enrollment
n’inclut pas d’informations relatives aux notes, elle doit uniquement contenir les deux clés étrangères CourseID
et StudentID
. Dans ce cas, il correspondrait à une table de jointure plusieurs-à-plusieurs sans charge utile (ou une table de jointure pure) dans la base de données, et vous n’auriez pas à créer une classe de modèle pour elle du tout. Les Instructor
entités ont Course
ce type de relation plusieurs-à-plusieurs, et comme vous pouvez le voir, il n’existe aucune classe d’entité entre elles :
Toutefois, une table de jointure est requise dans la base de données, comme indiqué dans le diagramme de base de données suivant :
Entity Framework crée automatiquement la CourseInstructor
table et vous la lisez et la mettez à jour indirectement en lisant et en mettant à jour les propriétés de navigation et Course.Instructors
de Instructor.Courses
navigation.
Schéma entité/association
L’illustration suivante montre le diagramme que les outils Entity Framework Power Tools créent pour le modèle School complet.
Outre les lignes de relation plusieurs-à-plusieurs (* à *) et les lignes de relation un-à-plusieurs (1 à *), vous pouvez voir ici la ligne de relation un-à-zéro-ou-un (1 à 0..1) entre les Instructor
entités et OfficeAssignment
la ligne de relation zéro-ou-un-à-plusieurs (0..1 à *) entre les entités Instructeur et Département.
Ajouter du code au contexte de base de données
Ensuite, vous allez ajouter les nouvelles entités à la SchoolContext
classe et personnaliser certains mappages à l’aide d’appels d’API Fluent. L’API est « fluent », car elle est souvent utilisée par la chaîne d’une série d’appels de méthode en une seule instruction, comme dans l’exemple suivant :
modelBuilder.Entity<Course>()
.HasMany(c => c.Instructors).WithMany(i => i.Courses)
.Map(t => t.MapLeftKey("CourseID")
.MapRightKey("InstructorID")
.ToTable("CourseInstructor"));
Dans ce tutoriel, vous allez utiliser l’API Fluent uniquement pour le mappage de base de données que vous ne pouvez pas faire avec les attributs. Toutefois, vous pouvez également utiliser l’API Fluent pour spécifier la majorité des règles de mise en forme, de validation et de mappage que vous pouvez spécifier à l’aide d’attributs. Certains attributs, tels que MinimumLength
, ne peuvent pas être appliqués avec l’API Fluent. Comme mentionné précédemment, MinimumLength
ne modifie pas le schéma, il applique uniquement une règle de validation côté client et côté serveur
Certains développeurs préfèrent utiliser exclusivement l’API Fluent pour pouvoir garder leurs classes d’entité « propres ». Vous pouvez mélanger des attributs et une API Fluent si vous le souhaitez. Il existe quelques personnalisations qui ne peuvent être effectuées qu’à l’aide de l’API Fluent. Toutefois, en règle générale, il est recommandé de choisir l’une de ces deux approches, et de l’utiliser de manière cohérente dans la mesure du possible.
Pour ajouter les nouvelles entités au modèle de données et effectuer un mappage de base de données que vous n’avez pas fait à l’aide d’attributs, remplacez le code dans DAL\SchoolContext.cs par le code suivant :
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"));
}
}
}
La nouvelle instruction de la méthode OnModelCreating configure la table de jointure plusieurs-à-plusieurs :
Pour la relation plusieurs-à-plusieurs entre les entités et
Course
lesInstructor
entités, le code spécifie les noms de table et de colonne pour la table de jointure. Code First peut configurer la relation plusieurs-à-plusieurs pour vous sans ce code, mais si vous ne l’appelez pas, vous obtiendrez des noms par défaut tels queInstructorInstructorID
pour laInstructorID
colonne.modelBuilder.Entity<Course>() .HasMany(c => c.Instructors).WithMany(i => i.Courses) .Map(t => t.MapLeftKey("CourseID") .MapRightKey("InstructorID") .ToTable("CourseInstructor"));
Le code suivant fournit un exemple de la façon dont vous pourriez avoir utilisé l’API Fluent au lieu d’attributs pour spécifier la relation entre les entités et OfficeAssignment
les Instructor
entités :
modelBuilder.Entity<Instructor>()
.HasOptional(p => p.OfficeAssignment).WithRequired(p => p.Instructor);
Pour plus d’informations sur les instructions « fluent API » qui font en arrière-plan, consultez le billet de blog de l’API Fluent.
Remplir la base de données avec des données de test
Remplacez le code dans le fichier Migrations\Configuration.cs par le code suivant pour fournir des données initiales pour les nouvelles entités que vous avez créées.
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));
}
}
}
Comme vous l’avez vu dans le premier tutoriel, la plupart de ce code met simplement à jour ou crée de nouveaux objets d’entité et charge des exemples de données dans des propriétés comme requis pour les tests. Toutefois, notez comment l’entité Course
, qui a une relation plusieurs-à-plusieurs avec l’entité Instructor
, est gérée :
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();
Lorsque vous créez un Course
objet, vous initialisez la Instructors
propriété de navigation en tant que collection vide à l’aide du code Instructors = new List<Instructor>()
. Cela permet d’ajouter Instructor
des entités associées à cela Course
à l’aide de la Instructors.Add
méthode. Si vous n’avez pas créé de liste vide, vous ne pourrez pas ajouter ces relations, car la Instructors
propriété serait null et n’aurait pas de Add
méthode. Vous pouvez également ajouter l’initialisation de liste au constructeur.
Ajouter une migration
À partir du PMC, entrez la add-migration
commande (ne faites pas encore la update-database
commande) :
add-Migration ComplexDataModel
Si vous tentiez d’exécuter la commande update-database
à ce stade (ne le faites pas encore), vous obtiendriez l’erreur suivante :
L’instruction ALTER TABLE est en conflit avec la contrainte FOREIGN KEY « FK_dbo.Course_dbo.Department_DepartmentID ». Le conflit s’est produit dans la base de données « ContosoUniversity », table « dbo.Department », colonne « DepartmentID ».
Parfois, lorsque vous exécutez des migrations avec des données existantes, vous devez insérer des données stub dans la base de données pour répondre aux contraintes de clé étrangère, et c’est ce que vous devez faire maintenant. Le code généré dans la méthode ComplexDataModel Up
ajoute une clé étrangère non nullable DepartmentID
à la Course
table. Étant donné qu’il existe déjà des lignes dans la Course
table lorsque le code s’exécute, l’opération AddColumn
échoue, car SQL Server ne sait pas quelle valeur placer dans la colonne qui ne peut pas être null. Par conséquent, vous devez modifier le code pour donner à la nouvelle colonne une valeur par défaut et créer un service stub nommé « Temp » pour agir comme service par défaut. Par conséquent, les lignes existantes Course
sont toutes liées au service « Temp » après l’exécution de la Up
méthode. Vous pouvez les associer aux services appropriés dans la Seed
méthode.
Modifiez l’horodatage<>_ComplexDataModel.cs fichier, commentez la ligne de code qui ajoute la colonne DepartmentID à la table Cours et ajoutez le code mis en surbrillance suivant (la ligne commentée est également mise en surbrillance) :
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));
Lorsque la Seed
méthode s’exécute, elle insère des lignes dans la Department
table et associe les lignes existantes Course
à ces nouvelles Department
lignes. Si vous n’avez pas ajouté de cours dans l’interface utilisateur, vous n’avez plus besoin du service « Temp » ou de la valeur par défaut sur la Course.DepartmentID
colonne. Pour permettre à quelqu’un d’avoir ajouté des cours à l’aide de l’application, vous souhaitez également mettre à jour le Seed
code de méthode pour vous assurer que toutes les Course
lignes (pas seulement celles insérées par des exécutions antérieures de la Seed
méthode) ont des valeurs valides DepartmentID
avant de supprimer la valeur par défaut de la colonne et de supprimer le service « Temp ».
Mettre à jour la base de données
Une fois que vous avez terminé de modifier le <fichier timestamp>_ComplexDataModel.cs , entrez la update-database
commande dans le PMC pour exécuter la migration.
update-database
Remarque
Il est possible d’obtenir d’autres erreurs lors de la migration de données et d’apporter des modifications de schéma. Si vous obtenez des erreurs de migration que vous ne pouvez pas résoudre, vous pouvez changer le nom de la base de données dans la chaîne de connexion ou supprimer la base de données. L’approche la plus simple consiste à renommer la base de données dans le fichier Web.config . L’exemple suivant montre le nom modifié en CU_Test :
<add name="SchoolContext" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=CU_Test;Integrated Security=SSPI;"
providerName="System.Data.SqlClient" />
Avec une nouvelle base de données, il n’y a pas de données à migrer, et la update-database
commande est beaucoup plus susceptible de se terminer sans erreurs. Pour obtenir des instructions sur la suppression de la base de données, consultez Comment supprimer une base de données de Visual Studio 2012.
Si cela échoue, une autre chose que vous pouvez essayer est de réinitialiser la base de données en entrant la commande suivante dans le PMC :
update-database -TargetMigration:0
Ouvrez la base de données dans l’Explorateur de serveurs comme vous l’avez fait précédemment, puis développez le nœud Tables pour voir que toutes les tables ont été créées. (Si vous avez toujoursL’Explorateur de serveurs s’ouvre à partir de l’heure précédente, cliquez sur le bouton Actualiser .)
Vous n’avez pas créé de classe de modèle pour la CourseInstructor
table. Comme expliqué précédemment, il s’agit d’une table de jointure pour la relation plusieurs-à-plusieurs entre les entités et Course
les Instructor
entités.
Cliquez avec le bouton droit sur la CourseInstructor
table et sélectionnez Afficher les données de table pour vérifier qu’elle contient des données en raison des Instructor
entités que vous avez ajoutées à la Course.Instructors
propriété de navigation.
Obtenir le code
Ressources supplémentaires
Vous trouverez des liens vers d’autres ressources Entity Framework dans le ASP.NET Accès aux données - Ressources recommandées.
Étapes suivantes
Dans ce tutoriel, vous allez :
- Personnalisé du modèle de données
- Entité Student mise à jour
- Créer une entité Instructor
- Créer une entité OfficeAssignment
- Modification de l’entité Course
- Création de l’entité Department
- Modification de l’entité d’inscription
- Ajout de code au contexte de base de données
- Remplir la base de données avec des données de test
- Ajouter une migration
- Base de données mise à jour
Passez à l’article suivant pour découvrir comment lire et afficher les données associées que Entity Framework charge dans les propriétés de navigation.