Accès rapide :
La base de données
Installer le framework
Définir le mapping
Le context Entity Framework
On configure le projet
On manipule nos entités
Le code complet de cet exemple
Entity Framework est l'ORM (Object Relational Mapper) officiel de la plate-forme .NET. Il permet de « mapper » vos classes (vos entités) à des
tables en base de données pour en exploiter ses données. La responsabilité de l'ORM Entity Framework sera de produire les ordres SQL (select
,
insert
, update
et delete
) à envoyer en base de données à votre place pour effectuer les traitements.
Afin de comprendre l'intérêt de cette solution, nous allons chercher à charger des commandes stockées en base de données (pourquoi pas pour un site de vente en ligne).
Ces commandes sont réparties sur quatre tables en base de données. Voici les codes SQL Server permettant de produire ces tables dans une base de données
que nous nommerons WebStore
.
CREATE TABLE [dbo].[T_Articles]( [IdArticle] [int] PRIMARY KEY IDENTITY(1,1), [Description] [varchar](50) NOT NULL, [Brand] [varchar](50) NOT NULL, [UnitaryPrice] [float] NOT NULL ); CREATE TABLE [dbo].[T_Users]( [IdUser] [int] IDENTITY(1,1) PRIMARY KEY IDENTITY(1,1), [Login] [varchar](50) NOT NULL, [Password] [varchar](50) NOT NULL, [ConnectionNumber] [int] NOT NULL, ); CREATE TABLE [dbo].[T_CommandLines]( [IdCommandLine] [int] PRIMARY KEY IDENTITY(1,1), [IdCommand] [int] NOT NULL, [IdArticle] [int] NOT NULL, [Quantity] [int] NOT NULL ); CREATE TABLE [dbo].[T_Commands]( [IdCommand] [int] PRIMARY KEY IDENTITY(1,1), [IdUser] [int] NOT NULL, [CommandDate] [datetime] NULL );
Remplissez ces bases de données avec quelques commandes, utilisateurs et articles.
Entity Framework, depuis sa version 6.0, est un projet autonome développé par Microsoft. Il s'installe sous forme d'un paquet NuGet. Veuillez procéder à l'installation du module dans votre projet (clic bouton droit sur le projet, puis sélectionnez « Gérer les dépendances NuGet »).
L'exemple de code suivant définit le mapping de la classe Article
: elle est associée à la table T_Articles
en base de données.
Par défaut, les propriétés d'une classe d'entité sont automatiquement mappées à des colonnes de mêmes noms. Dans notre exemple, il faut compléter le
mapping pour la propriété IdArticle
, pour indiquer qu'il s'agit de la clé primaire, et pour la propriété Price
, car en base
de données, le nom de la colonne est différent (UnitaryPrice
).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
[Table("T_Articles")] public class Article { [Column("IdArticle")] [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int IdArticle { get; set; } public string Description { get; set; } public string Brand { get; set; } [Column("UnitaryPrice")] public double Price { get; set; } public override string ToString() { return $"{IdArticle}: {Description} de marque {Brand} == {Price}"; } } |
La classe précédente est simple à mapper, car elle ne possède aucune relation vers d'autres éléments.
Pour l'entité CommandLine
(en lien avec la table T_CommandLines
), c'est un peu plus compliqué.
A partir de l'identifiant d'article, il faut pouvoir retrouver l'instance associée.
A partir de l'identifiant de commande, il faut pouvoir retrouver la commande associée.
Pour définir ces relations, nous allons utiliser l'attribut .NET [ForeignKey]
.
Voici le mapping pour cette classe d'entité.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
[Table("T_CommandLines")] public class CommandLine { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int IdCommandLine { get; set; } [ForeignKey("Command")] public int IdCommand { get; set; } public virtual Command Command { get; set; } [ForeignKey("Article")] public int IdArticle { get; set; } public virtual Article Article { get; set; } public int Quantity { get; set; } public override string ToString() { return $"{Quantity} x " + Article; } } |
Les entités Command
et User
doivent respectivement être mappées aux tables T_Commands
et T_Users
.
A vous de faire.
Pour manipuler des entités, il faut définir un contexte Entity Framework
.
De plus, un contexte se crée normalement via une fabrique. Voici le code associé à cette étape.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
public class ApplicationDbContext : DbContext { public ApplicationDbContext(string connectionString) : base(connectionString) { } public DbSet<Article> Articles { get; set; } public DbSet<CommandLine> CommandLines { get; set; } public DbSet<User> Users { get; set; } public DbSet<Command> Commands { get; set; } } public class ApplicationDbContextFactory : IDbContextFactory<ApplicationDbContext> { private static readonly string connectionString = ConfigurationManager.ConnectionStrings["WebStore"].ConnectionString; private readonly ApplicationDbContext m_dbContext = new ApplicationDbContext(connectionString); public ApplicationDbContext Create() { return m_dbContext; } } |
Comme vous l'aurez remarqué dans l'exemple ci-dessus, il est nécessaire de définir la chaîne de connexion à la base de données dans le fichier de
configuration de votre application (le fichier App.config
). Ajoutez-y une section <connectionStrings>
. Si vous
avez sécurisé l'accès à la base de données, veuillez y rajouter les informations d'authentification.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<?xml version="1.0" encoding="utf-8"?> <configuration> <configSections> <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 --> <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" /> </configSections> <connectionStrings> <add name="WebStore" connectionString="Data Source=DESKTOP-TPBD9IJ;Initial Catalog=WebStore;Integrated Security=True;;MultipleActiveResultSets=true" providerName="System.Data.SqlClient" /> </connectionStrings> <!-- Suite de la configuration --> </configuration> |
System.Configuration
.
Voici un exemple d'utilisation des entités précédemment présentées. Notez qu'il est possible d'afficher sur la console les requêtes SQL produites par Entity Framework en activant les logs.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
class Program { static void Main(string[] args) { using (ApplicationDbContext context = new ApplicationDbContextFactory().Create()) { // Pour activer les logs et voir les requêtes SQL produites //context.Database.Log = Console.WriteLine; // On récupére toutes les commandes possédant plus d'une ligne de commande // var set = context.Set<Command>(); IEnumerable<Command> set = from c in context.Commands where c.Lines.Count>1 select c; foreach (var data in set) { Console.WriteLine(data); } // On récupère l'article 1 Article article = (from art in context.Articles where art.IdArticle == 1 select art).Single(); Console.WriteLine(article); // On le met à jour article.Price += 0.5; Console.WriteLine(article); // On pousse la modification en base de données context.SaveChanges(); } } } |
Et voici les résultats produits par ce Main
.
1: Bond[James] - 0 @ 19/12/2018 23:29:04 10 x 1: Bonbon de marque Haribo == 0,5 5 x 2: Stylo Bleu de marque Bic == 1 1: Bonbon de marque Haribo == 0,5 1: Bonbon de marque Haribo == 1
Voici le code C# complet associé à l'exemple proposé.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 |
using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Configuration; using System.Data.Entity; using System.Data.Entity.Infrastructure; using System.Linq; using System.Text; namespace TestEntityFramework { public class ApplicationDbContext : DbContext { public ApplicationDbContext(string connectionString) : base(connectionString) { } public DbSet<Article> Articles { get; set; } public DbSet<CommandLine> CommandLines { get; set; } public DbSet<User> Users { get; set; } public DbSet<Command> Commands { get; set; } } public class ApplicationDbContextFactory : IDbContextFactory<ApplicationDbContext> { private static readonly string connectionString = ConfigurationManager.ConnectionStrings["WebStore"].ConnectionString; private readonly ApplicationDbContext m_dbContext = new ApplicationDbContext(connectionString); public ApplicationDbContext Create() { return m_dbContext; } } [Table("T_Articles")] public class Article { [Column("IdArticle")] [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int IdArticle { get; set; } public string Description { get; set; } public string Brand { get; set; } [Column("UnitaryPrice")] public double Price { get; set; } public override string ToString() { return $"{IdArticle}: {Description} de marque {Brand} == {Price}"; } } [Table("T_CommandLines")] public class CommandLine { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int IdCommandLine { get; set; } [ForeignKey("Command")] public int IdCommand { get; set; } public virtual Command Command { get; set; } [ForeignKey("Article")] public int IdArticle { get; set; } public virtual Article Article { get; set; } public int Quantity { get; set; } public override string ToString() { return $"{Quantity} x " + Article; } } [Table("T_Users")] public class User { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int IdUser { get; set; } public string Login { get; set; } public string Password { get; set; } public int ConnectionNumber { get; set; } public override string ToString() { return $"{IdUser}: {Login}[{Password}] - {ConnectionNumber}"; } } [Table("T_Commands")] public class Command { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int IdCommand { get; set; } [ForeignKey("User")] public int IdUser { get; set; } public virtual User User { get; set; } [InverseProperty("Command")] public virtual List<CommandLine> Lines { get; set; } public DateTime CommandDate { get; set; } public override string ToString() { StringBuilder builder = new StringBuilder(); builder.Append(User.ToString()).Append(" @ ").Append(CommandDate.ToString()).Append("\n"); foreach (CommandLine line in Lines) { builder.Append("\t").Append(line.ToString()).Append("\n"); } //builder.Append("Total == "); return builder.ToString(); } } class Program { static void Main(string[] args) { using (ApplicationDbContext context = new ApplicationDbContextFactory().Create()) { // Pour activer les logs et voir les requêtes SQL produites //context.Database.Log = Console.WriteLine; // On récupére toutes les commandes possédant plus d'une ligne de commande // var set = context.Set<Command>(); IEnumerable<Command> set = from c in context.Commands where c.Lines.Count>1 select c; foreach (var data in set) { Console.WriteLine(data); } // On récupère l'article 1 Article article = (from art in context.Articles where art.IdArticle == 1 select art).Single(); Console.WriteLine(article); // On le met à jour article.Price += 0.5; Console.WriteLine(article); // On pousse la modification en base de données context.SaveChanges(); } } } } |
Améliorations / Corrections
Vous avez des améliorations (ou des corrections) à proposer pour ce document : je vous remerçie par avance de m'en faire part, cela m'aide à améliorer le site.
Emplacement :
Description des améliorations :