Rechercher
 

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 :

Exemple de mapping « Entity Framework »

Accès rapide :
La base de données
Installer le framewok
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.

La base de données

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.

Installer le framewok

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 »).

Définir le mapping

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}";
    }
}
Mapping de l'entité Article

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;
     }
 
}
Mapping de l'entité CommandLine

Les entités Command et User doivent respectivement être mappées aux tables T_Commands et T_Users. A vous de faire.

Le context Entity Framework

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;
    }

}
Définition de la classe de contexte et de sa fabrique

On configure le projet

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.confif). 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>
Le fichier de configuration App.config
pour que le projet compile correctement, il est nécessaire d'y ajouter une référence sur l'assemblage System.Configuration.

On manipule nos entités

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();
        }

    }
}
On utilise nos entités

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

Le code complet de cet exemple

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();
            }

        }
    }
}
Le code complet