ASP.
NET MVC — Cours de développement web Chapitre 4 : Entity Framework & Base de données
Chapitre 4
Gérer la base de données avec Entity
Framework Code First
Objectifs du chapitre
• Comprendre ce qu'est un ORM et pourquoi Entity Framework est incontournable
• Maîtriser les trois approches de création d'une base de données (Code First, Database
First, Model First)
• Créer des entités, gérer les relations (1→n, n→n) et persister les données
• Utiliser les migrations pour faire évoluer votre base de données sans tout casser
• Générer des jeux de données de test et de production professionnels
Partie 1 — Comprendre les ORM et Entity
Framework
4.1. Qu'est-ce qu'un ORM ?
4.1.1. Le problème que l'ORM résout
Imaginez que vous deviez stocker des articles de blog dans une base de données SQL. Sans
ORM, vous écririez des requêtes SQL à la main, géreriez manuellement la connexion, itéreriez
sur les résultats ligne par ligne... Beaucoup de code répétitif et source d'erreurs.
💡 Définition — ORM (Object-Relational Mapping)
Un ORM (Object-Relational Mapping) est un outil qui fait le pont entre deux mondes :
• Le monde Objet : vos classes C# (Article, Commentaire, Tag...)
• Le monde Relationnel : les tables SQL de votre base de données
Avec un ORM, vous ne manipulez plus des lignes et des colonnes SQL,
vous manipulez directement vos objets C# — l'ORM se charge du reste.
4.1.2. Architecture d'un ORM .NET
Dans le monde .NET, un ORM s'articule généralement en deux couches :
• Un gestionnaire d'objets/données (le mapper) : il fait correspondre vos classes C# aux
tables SQL
• Un traducteur LINQ to SQL : il convertit vos requêtes LINQ en SQL optimisé
Ce traducteur permet ensuite à l'ORM de se connecter à n'importe quel SGBD via des
connecteurs spécifiques (SQL Server, MySQL, PostgreSQL, Oracle...) :
Page 1 / 24
[Link] MVC — Cours de développement web Chapitre 4 : Entity Framework & Base de données
Figure 4.1 — Architecture d'un ORM .NET : du manager d'objets aux connecteurs de base de données
4.2. Entity Framework — L'ORM officiel de Microsoft
4.2.1. Pourquoi choisir Entity Framework ?
Entity Framework (EF) est l'ORM officiel développé par Microsoft. Pour ce cours, c'est notre
choix numéro un, et voici pourquoi :
• Produit officiel Microsoft, régulièrement mis à jour (version 6 en cours, EF Core
pour .NET 5+)
• Documentation exhaustive en français et en anglais
• Parfaitement intégré à .NET : validation, exceptions, LINQ, [Link] Identity...
• Basé sur [Link], la couche d'accès aux données standard de .NET
• Complet mais accessible aux débutants : la courbe d'apprentissage est douce
✔ Bonne nouvelle
Vous n'avez pas besoin d'un ORM pour utiliser [Link] MVC. Mais une fois que vous en utiliserez
un, vous ne voudrez plus jamais retourner aux requêtes SQL manuelles.
4.2.2. Les alternatives à Entity Framework
Il existe deux grandes familles d'ORM pour .NET :
ORM complets — Accèdent au meilleur des deux mondes (objet + relationnel) et gèrent toutes
les complexités des relations. Exemples :
• Entity Framework (Microsoft) — notre choix
• NHibernate — portage .NET de l'ORM Hibernate Java, basé lui aussi sur [Link]
ORM légers — Se concentrent sur les opérations CRUD basiques et laissent plus de contrôle
au développeur. Exemples :
Page 2 / 24
[Link] MVC — Cours de développement web Chapitre 4 : Entity Framework & Base de données
• PetaPOCO — conçu pour travailler avec des POCO (Plain Old C# Objects) sans
dépendances
• Dapper — micro-ORM créé par l'équipe Stack Overflow, ultra-performant
🔑 Vocabulaire : POCO
POCO = Plain Old CLR/C# Object
Un POCO est une classe C# simple, sans aucune dépendance vers un ORM ou un framework.
C'est l'idéal en termes de conception, car cela respecte le principe de responsabilité unique (SRP).
Exemple : public class Article { public int ID {get;set;} public string Titre {get;set;} }
Partie 2 — Les trois approches pour créer une base
de données
Entity Framework propose trois façons de travailler avec une base de données. Avant de coder,
il est essentiel de choisir la bonne approche selon votre contexte de projet.
⚠ Attention
Une base de données, ça se réfléchit ! Elle détermine les performances et la structure de toute votre
application. Ne la créez jamais à la va-vite — prenez le temps d'analyser vos besoins.
4.3. L'approche Database First
Cette approche part d'une base de données SQL déjà existante. Visual Studio va analyser cette
base par rétro-ingénierie et générer automatiquement les classes C# correspondantes (Modèle
Logique de Données).
Quand l'utiliser : vous reprenez un projet existant avec une BDD déjà en production, ou votre
équipe comporte un DBA (Database Administrator) qui a déjà conçu le schéma SQL.
4.4. L'approche Model First
Cette approche utilise un diagramme graphique (schéma entité-relation, MERISE...) pour
décrire les tables et leurs relations. Visual Studio génère ensuite à la fois la base de données
SQL et les classes C# à partir de ce schéma.
Quand l'utiliser : un expert base de données travaille sur votre projet et préfère concevoir le
modèle visuellement avant de coder.
Page 3 / 24
[Link] MVC — Cours de développement web Chapitre 4 : Entity Framework & Base de données
4.5. L'approche Code First ✅ Notre choix
Cette approche part du code C#. Vous écrivez vos classes comme d'habitude — Entity
Framework se charge de créer et de maintenir la base de données correspondante. C'est la
plus naturelle pour un développeur.
💡 Pourquoi Code First ?
Ce qu'un développeur sait faire de mieux, c'est coder.
Avec Code First, vous restez dans votre élément : vous écrivez des classes C#
et Entity Framework fait le travail de DBA pour vous.
De plus, Code First est compatible avec Git : tout le schéma de BDD
est décrit en code, donc versionnable et partageable avec votre équipe.
L'approche Code First repose sur trois outils complémentaires :
1. Un mapper — fait correspondre vos classes C# aux tables SQL
2. Une API de description — permet de décrire les relations complexes (Annotations ou
Fluent API)
3. Les migrations — des fichiers de code qui décrivent comment évolue votre BDD
4.6. Comment choisir son approche ? — Arbre de décision
Pas sûr de quelle approche choisir ? Ce schéma vous guidera :
Page 4 / 24
[Link] MVC — Cours de développement web Chapitre 4 : Entity Framework & Base de données
Figure 4.2 — Arbre de décision : Database First, Model First ou Code First ?
Critère Database First Model First Code First
Point de départ Base SQL existante Diagramme/MERISE Classes C#
Profil idéal DBA / reprise Expert BDD Développeur C#
Génération auto Modèles C# BDD + Modèles BDD via migrations
Flexibilité Faible Moyenne Élevée
Versionnable (Git) Difficile Difficile Oui (migrations)
Partie 3 — Les entités : vos classes C# persistent
en base
4.7. Qu'est-ce qu'une entité ?
Dans le contexte d'Entity Framework, une entité est simplement une classe C# dont les
instances peuvent être sauvegardées (persistées) en base de données. En pratique, c'est une
classe normale avec quelques conventions à respecter.
🔑 Définition — Entité
Une entité est une classe C# publique qui représente un objet métier
(Article, Commentaire, Tag, Utilisateur...) et dont les données seront
Page 5 / 24
[Link] MVC — Cours de développement web Chapitre 4 : Entity Framework & Base de données
stockées dans une table SQL par Entity Framework.
Règle d'or : chaque entité doit avoir un identifiant unique (clé primaire),
par convention nommé ID ou <NomClasse>ID.
4.7.1. Structure minimale d'une entité
Pour notre blog, voici la structure de base de la classe Article :
Listing 4.1 — Structure de base de la classe Article
public class Article
{
// Clé primaire (générée automatiquement par la BDD)
public int ID { get; set; }
public string Titre { get; set; }
public string Contenu { get; set; }
public string ThumbnailPath { get; set; }
public bool EstPublie { get; set; }
}
Types de données supportés nativement par Entity Framework (non exhaustif) :
• Types primitifs : int, long, float, double, decimal, bool, byte
• Chaînes de caractères : string
• Dates et durées : DateTime, DateTimeOffset, TimeSpan
• Tableaux d'octets : byte[]
⚠ Attention aux types incompatibles
Les struct C# ne sont pas compatibles avec Entity Framework par défaut. Utilisez uniquement des
types primitifs, des string, DateTime, ou vos propres classes.
4.7.2. Correspondance entité → table SQL
Par convention, Entity Framework génère automatiquement une table SQL à partir de votre
entité :
• Le nom de la table = le nom de votre classe (ex : Article → table Articles)
• Chaque propriété = une colonne de la table (même nom, type SQL déduit
automatiquement)
• La propriété ID = clé primaire auto-incrémentée
Résultat en base de données pour notre classe Article :
Page 6 / 24
[Link] MVC — Cours de développement web Chapitre 4 : Entity Framework & Base de données
Figure 4.3 — La table SQL générée automatiquement par Entity Framework à partir de la classe Article
4.8. Personnaliser ses entités avec les annotations
Entity Framework propose des annotations (attributs C#) pour contrôler précisément comment
vos classes sont mappées en SQL.
4.8.1. Renommer tables et colonnes
Listing 4.2 — Personnalisation des noms de tables et colonnes
[Table("blog_Article")] // La table s'appellera blog_Article
public class Article
{
public int ID { get; set; }
[Column("Title")] // La colonne s'appellera Title (pas Titre)
[StringLength(128)] // Limite à 128 caractères en SQL
public string Titre { get; set; }
[Column("Content")]
public string Texte { get; set; }
public string ThumbnailPath { get; set; }
}
4.8.2. Annotations essentielles
📋 Récapitulatif des annotations les plus importantes
[Key] — Désigne la clé primaire (si elle ne s'appelle pas ID)
[Required] — Champ obligatoire (interdit NULL en SQL)
[StringLength(n)] — Limite la longueur d'une chaîne
[Column("nom")] — Renomme la colonne en SQL
[Table("nom")] — Renomme la table en SQL
[Index(IsUnique=true)] — Crée un index unique (recherche rapide + unicité)
[DataType([Link])] — Indication du type pour la validation
[DatabaseGenerated(...)] — Contrôle comment la BDD génère la valeur
4.8.3. La Fluent API — pour les cas avancés
Pour les situations complexes que les annotations ne peuvent pas exprimer, Entity Framework
propose la Fluent API. Elle s'utilise dans la méthode OnModelCreating() du DbContext et offre
un contrôle total sur le mapping.
✔ Pour ce cours
Page 7 / 24
[Link] MVC — Cours de développement web Chapitre 4 : Entity Framework & Base de données
Nous utiliserons principalement les annotations, plus simples et suffisantes pour la grande majorité
des cas. La Fluent API sera introduite uniquement pour gérer les relations complexes (cycles,
suppressions en cascade).
4.9. Préparer le contexte de données — DbContext
4.9.1. Le DbContext : la porte d'entrée vers votre base de données
Le DbContext est l'objet central d'Entity Framework. C'est lui qui fait le lien entre votre
application C# et la base de données. Il gère les connexions, suit l'état des entités et exécute
les requêtes SQL.
💡 DbContext en pratique
Dans Visual Studio, le DbContext est déjà partiellement configuré si vous avez
créé un projet avec authentification. Vous trouverez une classe ApplicationDbContext
qui hérite de IdentityDbContext — elle-même un DbContext enrichi pour gérer les utilisateurs.
4.9.2. Créer la base de données (LocalDB pour le développement)
Pendant le développement, nous utilisons SQL Server LocalDB — une version légère de SQL
Server intégrée à Visual Studio, parfaite pour créer et détruire des bases de données
rapidement.
Étapes pour configurer votre base de données locale :
4. Clic droit sur le dossier App_Data → Ajouter → Nouvel élément
Figure 4.4 — Menu contextuel pour ajouter un nouvel élément dans Visual Studio
5. Dans la fenêtre qui s'ouvre, sélectionnez la catégorie Données puis Base de données
SQL Server. Nommez-la sqldb.
Page 8 / 24
[Link] MVC — Cours de développement web Chapitre 4 : Entity Framework & Base de données
Figure 4.5 — Fenêtre de création d'une base de données SQL Server dans Visual Studio
6. Ouvrez [Link] et mettez à jour la connectionString DefaultConnection pour pointer
vers [Link]
Listing 4.3 — Configuration de la connexion dans [Link]
<connectionStrings>
<add name="DefaultConnection"
connectionString="Data Source=(LocalDb)\v11.0;
AttachDbFilename=|DataDirectory|\[Link];
Initial Catalog=Blog;Integrated Security=True"
providerName="[Link]" />
</connectionStrings>
Page 9 / 24
[Link] MVC — Cours de développement web Chapitre 4 : Entity Framework & Base de données
4.9.3. Enregistrer une entité dans le DbContext
Pour qu'Entity Framework sache qu'il doit gérer une entité (et créer la table correspondante), il
faut déclarer un DbSet dans votre ApplicationDbContext :
Listing 4.4 — Déclaration d'un DbSet dans ApplicationDbContext
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false) { }
// Chaque DbSet = une table dans la BDD
public DbSet<Article> Articles { get; set; }
public DbSet<Commentaire> Commentaires { get; set; }
public DbSet<Tag> Tags { get; set; }
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
✔ Astuce
Dès que vous créez une nouvelle entité, ajoutez-la immédiatement au DbContext. Cela débloque le
scaffolding automatique de Visual Studio pour les contrôleurs et les vues.
Partie 4 — Manipuler les entités avec Entity
Framework
C'est ici que tout prend vie ! Nous allons apprendre à lire, filtrer, créer, modifier et supprimer des
données grâce à Entity Framework et LINQ.
💡 Comment Entity Framework gère vos objets
Entity Framework ne sauvegarde pas vos modifications immédiatement en base.
Il maintient un état pour chaque entité trackée dans le DbContext :
• Nouveau — l'entité vient d'être créée (Add)
• Modifié — une propriété a changé
• Supprimé — l'entité sera supprimée au prochain SaveChanges()
• Détaché — l'entité n'est plus suivie par le contexte
La persisitance réelle en base n'a lieu qu'à l'appel de [Link]().
Page 10 / 24
[Link] MVC — Cours de développement web Chapitre 4 : Entity Framework & Base de données
4.10. Lire et filtrer des données
4.10.1. Connecter le contrôleur à la base de données
La première étape dans votre contrôleur est d'instancier le contexte de données :
Listing 4.5 — Connexion au DbContext dans le contrôleur
public class ArticleController : Controller
{
// Instance du contexte — notre lien vers la base de données
private ApplicationDbContext bdd = [Link]();
// ... vos actions (List, Details, Create, Edit, Delete)
}
4.10.2. Obtenir tous les articles
Listing 4.6 — Récupérer la liste complète des articles
public ActionResult List()
{
// Une seule ligne pour récupérer tous les articles !
List<Article> liste = [Link]();
return View(liste);
}
🔑 ToList() vs IEnumerable
[Link] → requête SQL différée (pas encore exécutée)
[Link]() → exécute la requête et charge tout en mémoire
Pour les vues, utilisez ToList() pour avoir une List<Article>.
Pour les grandes quantités de données, IEnumerable peut être plus efficace.
4.10.3. Pagination avec Skip et Take
Pour afficher les articles page par page, LINQ fournit deux méthodes complémentaires :
• Skip(n) — saute les n premiers éléments
• Take(n) — prend les n éléments suivants
Listing 4.7 — Pagination avec OrderBy, Skip et Take
public ActionResult List(int page = 0)
{
const int ARTICLES_PAR_PAGE = 5;
List<Article> liste = [Link]
.OrderBy(a => [Link]) // 1. Toujours ordonner avant
de paginer
.Skip(page * ARTICLES_PAR_PAGE) // 2. Sauter les pages
précédentes
Page 11 / 24
[Link] MVC — Cours de développement web Chapitre 4 : Entity Framework & Base de données
.Take(ARTICLES_PAR_PAGE) // 3. Prendre la bonne
quantité
.ToList();
[Link] = page;
return View(liste);
}
⚠ Ordre obligatoire
Appelez toujours OrderBy() AVANT Skip() et Take(). Sans ordre défini, la base de données peut
retourner les résultats dans n'importe quel ordre, ce qui rendrait votre pagination imprévisible.
4.10.4. Filtrer avec Where
La méthode Where() prend une lambda expression qui retourne true ou false pour chaque
élément — seuls les éléments qui retournent true sont conservés.
Listing 4.8 — Filtrage des articles publiés
// N'afficher que les articles publiés
List<Article> articlespublies = [Link]
.Where(a => [Link]) // Filtre simple
.Where(a => [Link] > 0) // Filtres chaînables
.OrderBy(a => [Link])
.ToList();
// Ou avec plusieurs conditions dans un seul Where :
// .Where(a => [Link] && )
4.10.5. Trouver un article précis — FirstOrDefault et Find
Listing 4.9 — Récupérer un article par son ID
// Méthode 1 : Find — optimisée pour les clés primaires
Article article = [Link](id);
// Méthode 2 : FirstOrDefault — flexible pour n'importe quelle condition
Article article = [Link](a => [Link] == id);
// Dans les deux cas, retourne null si l'article n'existe pas
if (article == null)
{
return HttpNotFound(); // Renvoie une erreur 404
}
Page 12 / 24
[Link] MVC — Cours de développement web Chapitre 4 : Entity Framework & Base de données
4.11. Créer, modifier et supprimer des entités (CRUD)
4.11.1. Créer un article — Add + SaveChanges
Listing 4.10 — Création et persistance d'un article
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(ArticleCreation vm)
{
if (![Link]) return View(vm);
Article article = new Article
{
Titre = [Link],
Contenu = [Link],
EstPublie = true
};
[Link](article); // Marque l'entité comme "Nouveau"
[Link](); // Exécute l'INSERT SQL en base
return RedirectToAction("List");
}
4.11.2. Modifier un article — Entry + SaveChanges
Pour une modification, EF a besoin de savoir QUEL objet est modifié. On commence par
récupérer l'entité existante depuis la BDD, puis on annonce à EF qu'elle est modifiée :
Listing 4.11 — Modification d'un article
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(int id, ArticleCreation vm)
{
// 1. Récupérer l'entité telle qu'elle est en BDD
Article existing = [Link](id);
if (existing == null) return RedirectToAction("List");
// 2. Annoncer à EF que l'entité est modifiée
var entry = [Link](existing);
[Link] = [Link];
// 3. Appliquer les nouvelles valeurs
[Link] = [Link];
[Link] = [Link];
// 4. Persister les changements
[Link]();
return RedirectToAction("List");
}
Page 13 / 24
[Link] MVC — Cours de développement web Chapitre 4 : Entity Framework & Base de données
⚠ Toujours récupérer avant de modifier
Si vous créez un nouvel objet Article et appelez SaveChanges() sans passer par Find() d'abord, EF
peut interpréter ça comme une création, pas une modification.
4.11.3. Supprimer un article — Remove + SaveChanges
Listing 4.12 — Suppression d'un article
[HttpPost]
[ValidateAntiForgeryToken] // Protection CSRF obligatoire !
public ActionResult Delete(int id)
{
Article article = [Link](id);
if (article == null) return RedirectToAction("List");
[Link](article); // Marque comme supprimé
[Link](); // Exécute le DELETE SQL
return RedirectToAction("List");
}
✔ Sécurité
Utilisez toujours une requête POST (jamais GET) pour les suppressions, et protégez-vous contre la
faille CSRF avec [ValidateAntiForgeryToken]. Un lien GET pour supprimer = une catastrophe
potentielle si quelqu'un partage ce lien !
4.12. Le patron de conception Repository
Au fur et à mesure que votre projet grandit, copier-coller les accès au DbContext dans chaque
contrôleur devient ingérable. Le patron Repository résout ce problème en créant une couche
d'abstraction entre vos contrôleurs et Entity Framework.
💡 Pourquoi le Repository ?
• Centralise tout le code d'accès aux données en un seul endroit
• Facilite les tests unitaires (on peut remplacer EF par un faux repository)
• Respecte le principe de responsabilité unique : le contrôleur contrôle, le repository accède aux
données
• Permet de changer d'ORM sans toucher aux contrôleurs
Page 14 / 24
[Link] MVC — Cours de développement web Chapitre 4 : Entity Framework & Base de données
Figure 4.6 — Le cycle de développement recommandé : Analyse → Conception → Développement → Tests
Listing 4.13 — Interface générique pour un Repository
public interface IEntityRepository<T> where T : class
{
IEnumerable<T> GetList(int skip = 0, int limit = 10);
T Find(int id);
void Save(T entity);
void Delete(T entity);
void Delete(int id);
}
Listing 4.14 — Implémentation Entity Framework du Repository
public class EFArticleRepository : IEntityRepository<Article>
{
private ApplicationDbContext bdd = new ApplicationDbContext();
public IEnumerable<Article> GetList(int skip = 0, int limit = 10)
Page 15 / 24
[Link] MVC — Cours de développement web Chapitre 4 : Entity Framework & Base de données
{
return [Link](a => [Link]).Skip(skip).Take(limit);
}
public Article Find(int id) => [Link](id);
public void Save(Article entity)
{
Article existing = [Link]([Link]);
if (existing == null)
[Link](entity);
else
{
[Link](existing).State = [Link];
[Link](existing).[Link](entity);
}
[Link]();
}
public void Delete(Article entity) { [Link](entity);
[Link](); }
public void Delete(int id) => Delete([Link](id));
}
Partie 5 — Les relations entre entités
Une base de données relationnelle tire sa puissance des relations entre tables. Entity
Framework gère ces relations de façon transparente à partir de vos classes C#. Voyons les
deux relations les plus importantes.
4.13. Relation One-To-Many (1→n) : Articles et Commentaires
4.13.1. Modélisation
Un article peut avoir plusieurs commentaires, mais chaque commentaire appartient à un seul
article. C'est la relation la plus fréquente dans le monde des bases de données.
Schéma conceptuel
Article (1) ────────────── (n) Commentaire
Un Article a plusieurs Commentaires
Un Commentaire appartient à un seul Article (propriété Parent)
Listing 4.15 — Relation 1→n : Article et Commentaire
public class Commentaire
{
public int ID { get; set; }
Page 16 / 24
[Link] MVC — Cours de développement web Chapitre 4 : Entity Framework & Base de données
[DataType([Link])]
public string Email { get; set; }
[StringLength(75)]
public string Pseudo { get; set; }
[DataType([Link])]
public string Contenu { get; set; }
public DateTime Publication { get; set; }
// Clé étrangère — lien vers l'article parent
public int ArticleID { get; set; }
public virtual Article Parent { get; set; } // Navigation property
}
public class Article
{
public int ID { get; set; }
// ... autres propriétés ...
// virtual = active le Lazy Loading
public virtual List<Commentaire> Comments { get; set; }
}
🔑 Lazy Loading — Chargement à la demande
Le mot-clé virtual sur une propriété de navigation active le Lazy Loading.
Cela signifie qu'EF ne charge pas les commentaires en mémoire tant qu'on
n'y accède pas explicitement dans le code.
Sans virtual : vous devez utiliser .Include("Comments") dans votre requête.
Avec virtual : EF charge les commentaires automatiquement au premier accès.
⚠ N'abusez pas du Lazy Loading avec de gros volumes de données —
chaque accès peut déclencher une requête SQL supplémentaire !
4.13.2. Vues partielles pour les commentaires
Les commentaires peuvent s'afficher à différents endroits (page de détail d'un article, page de
modération...). Plutôt que de dupliquer le code HTML, utilisez une vue partielle.
Dans Visual Studio, créez une vue partielle _List dans le dossier Views/Commentaire :
(Fichier → Ajouter une Vue → nom : _List, modèle : List, classe : Commentaire, cocher "Créer
en tant que vue partielle")
✔ Convention
Page 17 / 24
[Link] MVC — Cours de développement web Chapitre 4 : Entity Framework & Base de données
Les vues partielles commencent par un underscore (_List, _Form...). C'est une convention [Link]
MVC que tout développeur reconnaîtra immédiatement.
4.14. Relation Many-To-Many (n→n) : Articles et Tags
4.14.1. Modélisation
Un article peut avoir plusieurs tags, et un tag peut être associé à plusieurs articles. C'est la
relation Many-To-Many.
Schéma conceptuel
Article (n) ──────────────── (n) Tag
Un Article peut avoir plusieurs Tags
Un Tag peut être associé à plusieurs Articles
Listing 4.16 — Relation n→n : classes Article et Tag
public class Tag
{
public int ID { get; set; }
public string Name { get; set; }
[StringLength(255)]
[Index(IsUnique = true)] // Slug unique pour identifier le tag
public string Slug { get; set; }
// Navigation vers les articles associés
public virtual List<Article> LinkedArticles { get; set; }
}
public class Article
{
// ... propriétés existantes ...
public virtual List<Commentaire> Comments { get; set; }
public virtual List<Tag> Tags { get; set; } // <-- Many-To-Many
}
💡 Comment EF gère le Many-To-Many ?
Entity Framework crée automatiquement une table de liaison (table de jonction)
en arrière-plan — vous n'avez rien à faire !
Par exemple, pour Articles ↔ Tags, EF crée une table TagArticles avec
deux colonnes : Article_ID et Tag_Slug (les clés étrangères croisées).
Cette table invisible vous permet de naviguer dans les deux sens :
[Link] → liste des tags de l'article
[Link] → liste des articles avec ce tag
Page 18 / 24
[Link] MVC — Cours de développement web Chapitre 4 : Entity Framework & Base de données
4.14.2. Gérer les tags depuis le contrôleur
Voici comment créer un article avec ses tags depuis le contrôleur :
Listing 4.17 — Création d'un article avec ses tags
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(ArticleCreation vm)
{
if (![Link]) return View(vm);
Article article = new Article
{
Titre = [Link],
Contenu = [Link],
Tags = new List<Tag>() // Important : initialiser la liste
};
// Traiter les tags (séparés par des virgules dans le formulaire)
if ()
{
SlugHelper slugifier = new SlugHelper();
foreach (string tagName in [Link](','))
{
string slug = [Link]([Link]());
// Chercher si le tag existe déjà en BDD
Tag existing = [Link](t => [Link] == slug);
[Link](existing ?? new Tag { Name=[Link](),
Slug=slug });
}
}
[Link](article);
[Link]();
return RedirectToAction("List");
}
Partie 6 — Les Migrations Code First
Vous en avez assez de supprimer votre base de données à chaque modification du modèle ?
Les migrations sont la solution. Elles permettent de faire évoluer votre BDD de façon
incrémentale, sans perdre les données existantes.
💡 Analogie — Les migrations, c'est comme Git pour votre base de données
Chaque migration est un fichier de code C# qui décrit précisément comment
passer de la version N à la version N+1 de votre schéma SQL.
Avantages :
Page 19 / 24
[Link] MVC — Cours de développement web Chapitre 4 : Entity Framework & Base de données
• Versionnables avec Git — toute l'équipe a le même schéma
• Réversibles — vous pouvez revenir en arrière à n'importe quelle version
• Automatiques — plus besoin de supprimer/recréer la BDD
• Compatibles avec les données existantes en production
4.15. Fonctionnement des migrations
Voici comment les migrations s'exécutent :
7. EF liste tous les fichiers de migration dans le dossier Migrations/
8. Il identifie la dernière migration appliquée (stockée dans une table __MigrationHistory en
BDD)
9. Pour chaque migration plus récente, il exécute la méthode Up() qui modifie le schéma
10. À la fin, il exécute la méthode Seed() pour insérer/mettre à jour les données de
référence
4.16. Commandes essentielles (Package Manager Console)
Commandes migrations — dans la Package Manager Console (outils → Gestionnaire de packages)
# 1. Activer les migrations (une seule fois par projet)
Enable-Migrations
# 2. Créer la première migration (après Enable-Migrations)
Add-Migration Initial
# 3. Après chaque modification de votre modèle, créer une migration
Add-Migration AjoutChampEstPublie
Add-Migration AjoutRelationTags
# (choisissez un nom lisible qui décrit le changement !)
# 4. Appliquer toutes les migrations en attente à la base de données
Update-Database
# 5. Revenir à une version précédente (optionnel)
Update-Database -TargetMigration: "Initial"
✔ Bonne pratique — Nommer ses migrations
Donnez des noms parlants à vos migrations : AjoutChampEstPublie, RelationArticleTag,
AjoutIndexSlug... Dans 6 mois, vous comprendrez d'un coup d'œil ce que fait chaque migration.
4.17. La méthode Seed — Pré-remplir votre base de données
Le fichier Migrations/[Link] contient une méthode Seed() qui est exécutée à chaque
Update-Database. Elle vous permet d'insérer automatiquement des données indispensables.
Page 20 / 24
[Link] MVC — Cours de développement web Chapitre 4 : Entity Framework & Base de données
4.17.1. Ajouter un article de test
Listing 4.18 — Méthode Seed avec un article de test
protected override void Seed(ApplicationDbContext context)
{
// Ajouter un article si la BDD est vide
[Link](
a => [Link], // Propriété utilisée pour détecter les doublons
new Article
{
Titre = "Mon premier article",
Contenu = "Bienvenue sur mon blog !",
EstPublie = true
}
);
[Link]();
}
4.17.2. Créer un compte administrateur
Listing 4.19 — Création du compte administrateur dans la Seed
// Créer le rôle Administrator s'il n'existe pas
if ()
{
var roleManager = new RoleManager<IdentityRole>(
new RoleStore<IdentityRole>(context));
[Link](new IdentityRole { Name = "Administrator" });
}
// Créer l'utilisateur admin s'il n'existe pas
if ()
{
var userManager = new UserManager<ApplicationUser>(
new UserStore<ApplicationUser>(context));
var admin = new ApplicationUser { UserName = "Admin" };
[Link](admin, "Admin@Pwd123!"); // Mot de passe fort
requis
[Link]([Link], "Administrator");
}
4.17.3. Données de test avec [Link]
Pour un jeu de données réaliste en développement, utilisez le package NuGet [Link] qui
génère automatiquement des données cohérentes :
Listing 4.20 — Génération de données de test avec [Link]
// Installer via NuGet : Install-Package [Link]
// Générer 10 articles avec tags aléatoires
for (int i = 0; i < 10; i++)
Page 21 / 24
[Link] MVC — Cours de développement web Chapitre 4 : Entity Framework & Base de données
{
Tag tag1 = new Tag { Name = [Link](1).First(),
Slug = [Link](1).First().ToLower() };
[Link](t => [Link], tag1);
Article article = new Article
{
Titre = [Link](),
Contenu = [Link](3),
Pseudo = [Link](),
EstPublie = true,
Tags = new List<Tag> { tag1 }
};
[Link](article);
}
[Link]();
4.17.4. Séparer jeu de test et données de production
Une bonne pratique consiste à distinguer les données de développement (beaucoup de fausses
données) des données de production (seulement les indispensables) :
Listing 4.21 — Séparation debug / production via [Link]
// Dans [Link] — ajouter dans <appSettings> :
<add key="data_version" value="debug"/>
// Dans [Link] :
<appSettings>
<add xdt:Transform="Replace" xdt:Locator="Match(key)"
key="data_version" value="debug"/>
</appSettings>
// Dans [Link] :
<appSettings>
<add xdt:Transform="Replace" xdt:Locator="Match(key)"
key="data_version" value="production"/>
</appSettings>
// Dans Migrations/[Link] :
protected override void Seed(ApplicationDbContext context)
{
string mode = [Link]["data_version"];
if (mode == "debug") SeedDebug(context);
else if (mode == "production") SeedProduction(context);
}
Page 22 / 24
[Link] MVC — Cours de développement web Chapitre 4 : Entity Framework & Base de données
Partie 7 — La Fluent API et les cas avancés
La Fluent API est l'outil de configuration avancée d'Entity Framework. Elle est nécessaire
lorsque les annotations ne peuvent pas exprimer la complexité d'une relation — notamment les
cycles de suppression en cascade.
⚠ Niveau avancé
Cette section s'adresse aux développeurs ayant une bonne connaissance des bases de données.
Dans la plupart des projets courants, vous n'en aurez pas besoin. Mais vous tomberez un jour sur le
cas problème décrit ici.
4.18. Le problème des cycles de suppression en cascade
Par défaut, Entity Framework configure la suppression en cascade : supprimer une entité parent
supprime automatiquement toutes ses entités enfants. C'est pratique... jusqu'au jour où votre
modèle contient un cycle de relations.
Exemple de cycle problématique
Entité A contient une liste de B
Entité B contient une référence vers C
Entité C contient une référence vers A
→ Supprimer A → supprime B → supprime C → qui voulait supprimer A (déjà supprimé !)
→ DEADLOCK ou erreur de contrainte SQL
La solution est d'utiliser la Fluent API pour indiquer à EF que certaines suppressions ne doivent
PAS être en cascade :
Listing 4.22 — Désactiver la suppression en cascade avec la Fluent API
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
[Link](modelBuilder); // Toujours appeler le
parent !
// Pour une relation entre A et B : désactiver la cascade
[Link]<A>()
.HasMany(a => [Link]) // A a plusieurs B
.WithOptional(b => [Link]) // La relation est
optionnelle
.WillCascadeOnDelete(false); // Pas de suppression en
cascade
}
}
Page 23 / 24
[Link] MVC — Cours de développement web Chapitre 4 : Entity Framework & Base de données
✔ Quand utiliser la Fluent API ?
• Relations avec cycle (A→B→C→A)
• Relations en losange (A→B→D et A→C→D)
• Nommage très personnalisé des colonnes de liaison (Many-To-Many)
• Contraintes SQL avancées non exprimables en annotations
• Performance : configurer précisément les index composites
Synthèse du Chapitre 4
Félicitations ! Vous avez maintenant toutes les bases pour gérer une base de données
relationnelle dans une application [Link] MVC. Voici ce que vous avez appris :
• ORM : Entity Framework traduit vos classes C# en tables SQL et inversement
• Trois approches : Code First (notre choix), Database First, Model First
• Entités : des classes C# publiques avec un ID, personnalisables via annotations
• DbContext : la porte d'entrée vers la BDD — déclarez vos DbSet<T> pour chaque entité
• CRUD : Add, Remove, Entry/SaveChanges pour créer, supprimer, modifier
• LINQ : Where, OrderBy, Skip, Take, FirstOrDefault pour interroger les données
• Relations 1→n : une propriété de navigation + virtual pour le Lazy Loading
• Relations n→n : deux listes croisées — EF crée la table de liaison automatiquement
• Migrations : versionnez votre schéma SQL en C# (Enable, Add, Update-Database)
• Seed : pré-remplissez votre BDD avec des données de test ou de production
• Fluent API : pour les cas avancés que les annotations ne peuvent pas gérer
🎯 Prochaine étape
Dans le chapitre suivant, nous allons améliorer notre application en ajoutant :
• L'authentification et la gestion des rôles (Admin, Auteur, Visiteur)
• La validation avancée des données avec des ModelBinders personnalisés
• L'optimisation des performances avec le caching et les requêtes N+1
Page 24 / 24