Tutoriel : Implémenter l’héritage avec EF dans une application ASP.NET MVC 5
Dans le tutoriel précédent, vous avez géré les exceptions d’accès concurrentiel. Ce didacticiel vous indiquera comment implémenter l’héritage dans le modèle de données.
Dans la programmation orientée objet, vous pouvez utiliser l’héritage pour faciliter la réutilisation du code. Dans ce didacticiel, vous allez modifier les classes Instructor
et Student
afin qu’elles dérivent d’une classe de base Person
qui contient des propriétés telles que LastName
, communes aux formateurs et aux étudiants. Vous n’ajouterez ni ne modifierez aucune page web, mais vous modifierez une partie du code et ces modifications seront automatiquement répercutées dans la base de données.
Dans ce tutoriel, vous allez :
- Apprendre à mapper l’héritage à la base de données
- Créer la classe Person
- Mettre à jour Student et Instructor
- Ajouter une personne au modèle
- Créer et mettre à jour des migrations
- Tester l’implémentation
- Déployer sur Azure
Prérequis
Mapper l’héritage à la base de données
Les Instructor
classes et Student
dans le School
modèle de données ont plusieurs propriétés identiques :
Supposons que vous souhaitez éliminer le code redondant pour les propriétés partagées par les entités Instructor
et Student
. Ou vous souhaitez écrire un service capable de mettre en forme les noms sans se soucier du fait que le nom provienne d’un formateur ou d’un étudiant. Vous pouvez créer une Person
classe de base qui contient uniquement ces propriétés partagées, puis faire en sorte que les Instructor
entités et Student
héritent de cette classe de base, comme illustré dans l’illustration suivante :
Il existe plusieurs façons de représenter cette structure d’héritage dans la base de données. Vous pouvez avoir une table Person
qui inclut des informations sur les étudiants et les formateurs dans une table unique. Certaines colonnes peuvent s’appliquer uniquement aux instructeurs (HireDate
), d’autres uniquement aux étudiants (EnrollmentDate
), d’autres aux deux (LastName
, FirstName
). En règle générale, vous disposez d’une colonne de discriminateur pour indiquer le type que chaque ligne représente. Par exemple, la colonne de discriminateur peut avoir « Instructor » pour les formateurs et « Student » pour les étudiants.
Ce modèle de génération d’une structure d’héritage d’entité à partir d’une table de base de données unique est appelé héritage table par hiérarchie (TPH).
Une alternative consiste à faire en sorte que la base de données ressemble plus à la structure d’héritage. Par exemple, vous pouvez avoir uniquement les champs de nom dans la table Person
, et des tables Instructor
et Student
distinctes avec les champs de date.
Ce modèle de création d’une table de base de données pour chaque classe d’entité est appelé héritage table par type (TPT).
Une autre option encore consiste à mapper tous les types non abstraits à des tables individuelles. Toutes les propriétés d’une classe, y compris les propriétés héritées, sont mappées aux colonnes de la table correspondante. Ce modèle porte le nom d’héritage TPC (table par classe concrète). Si vous avez implémenté l’héritage TPC pour les classes Person
, Student
et Instructor
comme indiqué précédemment, les tables Student
et Instructor
ne seraient pas différentes avant et après l’implémentation de l’héritage.
Les modèles d’héritage TPC et TPH offrent généralement de meilleures performances dans Entity Framework que les modèles d’héritage TPT, car les modèles TPT peuvent entraîner des requêtes de jointure complexes.
Ce didacticiel montre comment implémenter l’héritage TPH. TPH étant le modèle d’héritage par défaut dans Entity Framework, il vous suffit de créer une Person
classe, de modifier les Instructor
classes et Student
pour dériver de Person
, d’ajouter la nouvelle classe au DbContext
, et de créer une migration. (Pour plus d’informations sur l’implémentation des autres modèles d’héritage, consultez Mappage de l’héritage table par type (TPT) et Mappage de l’héritage TPC (Table-Per-Concrete Class) dans la documentation MSDN Entity Framework.)
Créer la classe Person
Dans le dossier Models , créez Person.cs et remplacez le code du modèle par le code suivant :
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace ContosoUniversity.Models
{
public abstract class Person
{
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; }
[Display(Name = "Full Name")]
public string FullName
{
get
{
return LastName + ", " + FirstMidName;
}
}
}
}
Mettre à jour Student et Instructor
Maintenant, mettez à jour Instructor.cs et Student.cs pour hériter des valeurs de la Person.sc.
Dans Instructor.cs, dérivez la Instructor
classe de la Person
classe et supprimez les champs clé et nom. Le code ressemblera à l’exemple suivant :
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace ContosoUniversity.Models
{
public class Instructor : Person
{
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
[Display(Name = "Hire Date")]
public DateTime HireDate { get; set; }
public virtual ICollection<Course> Courses { get; set; }
public virtual OfficeAssignment OfficeAssignment { get; set; }
}
}
Apportez des modifications similaires à Student.cs. La Student
classe ressemblera à l’exemple suivant :
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace ContosoUniversity.Models
{
public class Student : Person
{
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
[Display(Name = "Enrollment Date")]
public DateTime EnrollmentDate { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
}
Ajouter une personne au modèle
Dans SchoolContext.cs, ajoutez une DbSet
propriété pour le type d’entité Person
:
public DbSet<Person> People { get; set; }
C’est là tout ce dont Entity Framework a besoin pour configurer l’héritage TPH (table par hiérarchie). Comme vous le verrez, lorsque la base de données est mise à jour, elle a une Person
table à la Student
place des tables et .Instructor
Créer et mettre à jour des migrations
Dans la console du Gestionnaire de package (PMC), entrez la commande suivante :
Add-Migration Inheritance
Exécutez la Update-Database
commande dans le PMC. La commande échoue à ce stade, car nous avons des données existantes que les migrations ne savent pas comment gérer. Vous obtenez un message d’erreur semblable au suivant :
Impossible de supprimer l’objet 'dbo. Instructor' car il est référencé par une contrainte FOREIGN KEY.
Ouvrez Migrations< ; timestamp>_Inheritance.cs et remplacez la Up
méthode par le code suivant :
public override void Up()
{
// Drop foreign keys and indexes that point to tables we're going to drop.
DropForeignKey("dbo.Enrollment", "StudentID", "dbo.Student");
DropIndex("dbo.Enrollment", new[] { "StudentID" });
RenameTable(name: "dbo.Instructor", newName: "Person");
AddColumn("dbo.Person", "EnrollmentDate", c => c.DateTime());
AddColumn("dbo.Person", "Discriminator", c => c.String(nullable: false, maxLength: 128, defaultValue: "Instructor"));
AlterColumn("dbo.Person", "HireDate", c => c.DateTime());
AddColumn("dbo.Person", "OldId", c => c.Int(nullable: true));
// Copy existing Student data into new Person table.
Sql("INSERT INTO dbo.Person (LastName, FirstName, HireDate, EnrollmentDate, Discriminator, OldId) SELECT LastName, FirstName, null AS HireDate, EnrollmentDate, 'Student' AS Discriminator, ID AS OldId FROM dbo.Student");
// Fix up existing relationships to match new PK's.
Sql("UPDATE dbo.Enrollment SET StudentId = (SELECT ID FROM dbo.Person WHERE OldId = Enrollment.StudentId AND Discriminator = 'Student')");
// Remove temporary key
DropColumn("dbo.Person", "OldId");
DropTable("dbo.Student");
// Re-create foreign keys and indexes pointing to new table.
AddForeignKey("dbo.Enrollment", "StudentID", "dbo.Person", "ID", cascadeDelete: true);
CreateIndex("dbo.Enrollment", "StudentID");
}
Ce code prend en charge les tâches de mise à jour de base de données suivantes :
Supprime les contraintes de clé étrangère et les index qui pointent vers la table Student.
Renomme la table Instructor en Person et apporte les modifications nécessaires pour qu’elle stocke les données des étudiants :
- Ajoute une EnrollmentDate nullable pour les étudiants.
- Ajoute la colonne Discriminator pour indiquer si une ligne est pour un étudiant ou un formateur.
- Rend HireDate nullable étant donné que les lignes d’étudiant n’ont pas de dates d’embauche.
- Ajoute un champ temporaire qui sera utilisé pour mettre à jour les clés étrangères qui pointent vers les étudiants. Lorsque vous copiez des étudiants dans la table Person, ils obtiennent de nouvelles valeurs de clé primaire.
Copie des données à partir de la table Student dans la table Person. Cela entraîne l’affectation de nouvelles valeurs de clés primaires aux étudiants.
Corrige les valeurs de clés étrangères qui pointent vers les étudiants.
Crée de nouveau les index et les contraintes de clé étrangère, désormais pointées vers la table Person.
(Si vous aviez utilisé un GUID à la place d’un entier comme type de clé primaire, les valeurs des clés primaires des étudiants n’auraient pas changé, et plusieurs de ces étapes auraient pu être omises.)
Exécutez de nouveau la commande update-database
.
(Dans un système de production, vous apporteriez des modifications correspondantes à la méthode Down au cas où vous deviez l’utiliser pour revenir à la version précédente de la base de données. Pour ce tutoriel, vous n’utiliserez pas la méthode Down.)
Notes
Il est possible d’obtenir d’autres erreurs lors de la migration des données et de l’apport de modifications de schéma. Si vous obtenez des erreurs de migration que vous ne pouvez pas résoudre, vous pouvez poursuivre le didacticiel en modifiant le chaîne de connexion dans le fichier Web.config ou en supprimant la base de données. L’approche la plus simple consiste à renommer la base de données dans le fichier Web.config . Par exemple, remplacez le nom de la base de données par ContosoUniversity2, comme indiqué dans l’exemple suivant :
<add name="SchoolContext"
connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=ContosoUniversity2;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 vous suivez cette approche pour poursuivre le didacticiel, ignorez l’étape de déploiement à la fin de ce didacticiel ou déployez-le sur un nouveau site et une nouvelle base de données. Si vous déployez une mise à jour sur le même site que celui sur lequel vous avez déjà effectué le déploiement, EF obtient la même erreur quand il exécute automatiquement des migrations. Si vous souhaitez résoudre une erreur de migration, la meilleure ressource est l’un des forums ou StackOverflow.com Entity Framework.
Tester l’implémentation
Exécutez le site et essayez différentes pages. Tout fonctionne comme avant.
Dans Server Explorer, développez Data Connections\SchoolContext, puis Tables, et vous voyez que les tables Student et Instructor ont été remplacées par une table Person. Développez la table Person et vous voyez qu’elle contient toutes les colonnes qui se trouveraient auparavant dans les tables Student et Instructor .
Cliquez avec le bouton droit sur la table Person, puis cliquez sur Afficher les données de la table pour voir la colonne de discriminateur.
Le diagramme suivant illustre la structure de la nouvelle base de données School :
Déployer sur Azure
Cette section nécessite que vous ayez terminé la section facultative Déploiement de l’application sur Azure de la partie 3, Tri, filtrage et pagination de cette série de tutoriels. Si vous avez rencontré des erreurs de migration que vous avez résolues en supprimant la base de données dans votre projet local, ignorez cette étape ; ou créez un site et une base de données, puis déployez dans le nouvel environnement.
Dans l’Explorateur de solutions de Visual Studio, cliquez avec le bouton droit sur le projet, puis dans le menu contextuel, sélectionnez Publier.
Cliquez sur Publier.
L’application web s’ouvre dans votre navigateur par défaut.
Testez l’application pour vérifier qu’elle fonctionne.
La première fois que vous exécutez une page qui accède à la base de données, Entity Framework exécute toutes les méthodes de migration
Up
nécessaires pour mettre la base de données à jour avec le modèle de données actuel.
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.
Pour plus d’informations sur cette structure d’héritage et d’autres, consultez Modèle d’héritage TPT et Modèle d’héritage TPH sur MSDN. Dans le prochain didacticiel, vous allez apprendre à gérer divers scénarios Entity Framework relativement avancés.
Étapes suivantes
Dans ce tutoriel, vous allez :
- Appris à mapper l’héritage à la base de données
- Créez la classe Person
- Mettez à jour Student et Instructor
- Personne ajoutée au modèle
- Migrations créées et mises à jour
- Implémentation testée
- Déployé sur Azure
Passez à l’article suivant pour en savoir plus sur les sujets utiles à connaître lorsque vous allez au-delà des principes de base du développement d’applications web ASP.NET qui utilisent Entity Framework Code First.