Data

Entity Framework Code First

Nov 17, 2014

Sarah Bessard

L’Entity Framework est une technologie qui consiste en un ORM (object-relational mapping) qui permet d’interfacer avec une base de données SQL server via 3 approches selon le besoin (Database First, Model First et Code First).

Dans cet article, je vais étudier l’approche Entity Framework Code First qui est centrée autour du code. Cette approche nous permet d’écrire nos entités du modèle et d’en déduire automatiquement la base de données qu’EF se chargera de générer.

Avec Code First, Le designer Entity n’est pas du tout utilisé pour modifier les fichiers EDMX.

Architecture du projet :

Pour notre cas d’étude,  nous allons se baser sur l’architecture multicouche suivante :

Architecture

Notre projet ressemble donc à ceci :

Projet

Les éléments développés sont comme suit :

  • La Librairie d’entités (GestionFactures.Entity) : C’est la représentation objet des  données de notre application. Elle contient les classes représentant les POCOs (Poor Old CLR Objects) permettant de travailler avec les données de l’application.
  • La Data Logic Layer (GestionFactures.DAL) :  représente la couche d’accès aux données.
  • La Business Layer (GestionFactures.BLL) :  Cette couche contient les traitements métiers tels que les logs, les traitements des exceptions, ect. Elle représente une passerelle entre l’application et la couche d’accès aux données permettant ainsi de ne pas s’attaquer directement à la couche DAL.
  •  Le projet WEB (GestionFactures.Web) en ASP.Net MVC4: L’IHM de notre application.

Création des classes :

Nous allons d’abord commencer par créer les classes qui représentent les tables dans notre base de données.

Nous allons donc créer les deux classes suivantes dans la couche  « Entity » ( ref. GestiionFactures.Entity )

    public class Facture
    {
        public decimal IdFacture { get; set; }        
        public string NumFacture { get; set; }
        public string NumCommande { get; set; }
        public DateTime DateCreation { get; set; }
        public string DateCreationFormat { get { return DateCreation.ToString("dd/MM/yyyy"); } }
    }

    public class Fournisseur
    {
        public decimal IdFournisseur { get; set; }
        public string Name { get; set; }
    }

Ensuite, il faut référencer Entity Framework dans la couche DAL. Pour ceci, nous allons utiliser le gestionnaire des packages « NuGet » qui va télécharger et référencer dans la DAL la DLL de l’Entity Framework. Dans notre cas, nous allons utiliser la version 6.1 . Nuget

L’étape suivante serait de créer la classe  qui définit les entités qui vont être converties à des tables dans notre base de données. Cette classe hérite de DbContext et elle possède un attribut « DbSet »pour chaque type d’entité à manipuler.

  public class FacturationContext : DbContext
    {   
        public DbSet<Facture> Factures { get; set; }
        public DbSet<Fournisseur> Fournisseurs { get; set; }    

Maintenant, il faut définir dans la Couche DAL( Data Access Layer) la classe d’accès aux données qui manipule les tables en base de données avec les opérations de CRUD.

    public class FacturationDal
    {
        FacturationContext context = new FacturationContext();

        /// <summary>
        /// Retourne la liste des fournisseurs
        /// </summary>
        /// <returns>La liste retournée</returns>
        public List<Fournisseur> GetAll()
        {
            return context.Fournisseurs.ToList();
        }

        /// <summary>
        /// Insertion d'un fournisseur
        /// </summary>
        /// <param name="fournisseur">Fournisseur à insérer</param>
        /// <returns>Succès/échec</returns>
        public bool Insert(Fournisseur fournisseur)
        {
            context.Fournisseurs.Add(fournisseur);
            return context.SaveChanges() > 0;
        }


        /// <summary>
        /// Mise à jour d'un fournisseur
        /// </summary>
        /// <param name="fournisseur">Fournisseur à mettre à jour</param>
        /// <returns>Succès/échec</returns>
        public bool Update(Fournisseur fournisseur)
        {
            context.Entry(fournisseur).State = EntityState.Modified;
            return context.SaveChanges() > 0;
        }
        
        /// <summary>
        /// Suppression d'une facture
        /// </summary>
        /// <param name="facture">Facture à supprimer</param>
        /// <returns>Succès/échec</returns>
        public bool DeleteFacture(Facture facture)
        {
            context.Factures.Remove(facture);
            return  context.SaveChanges() > 0;
        }
    }

Nous allons par la suite configurer la chaîne de connexion dans le Web.config comme suit :

<configuration>
  <connectionStrings>
    <add name="FacturationContext" connectionString="data source=10.33.10.201; Initial Catalog=GestionFactures; 
         persist security info=false;packet size=4096;trusted_connection=yes;Connection Lifetime=120;" 
         providerName="System.Data.SqlClient" />
  </connectionStrings>

Le nom de la chaîne de connexion est le même que le nom de notre classe de contexte.

N’oublions pas aussi d’alimenter notre couche BLL, qui joue le rôle d’une passerelle entre le projet Web et la couche DAL. Dans cette couche, nous allons donc faire un simple appel à la DAL et ajouter certains traitements complémentaires tels que les logs, les exceptions ect.

J’ai utilisé Castle Windsor pour l’injection de dépendance dans mon projet mais je ne vais pas parler de ce concept dans cet article.

    public class FacturationRepository : IFacturationRepository
    {
        #region Properties   
        public IFacturationDal FacturationDal { get; set; }      
        public ILogger Logger { get; set; }
        #endregion

        /// <summary>
        /// Récupération de la liste des fournissseurs
        /// </summary>
        /// <param name="connectionString">La chaine de connexion à la bdd</param>
        /// <returns>La liste des fournisseurs</returns>
        public List<Fournisseur> GetFournisseur(string connectionString)
        {
            try
            {
                Logger.Debug("Récupération de la liste des fournisseurs...");
                return FacturationDal.GetFournisseur(connectionString);
            }
            catch (Exception ex)
            {
                Logger.Error("La récupération de la liste des fournisseurs a échoué: ", ex);
                return null;
            }
        }

Une fois l’application lancée, la base sera bien créée avec les noms des tables et attributs comme précisé dans l’exemple. Heureusement, EF est assez intelligent pour ajouter les clés primaires à partir des noms des variables, il prend en fait, l’attribut nommé « Id » ou qui contient au moins la chaîne « Id » dans son nom et le met comme clé primaire de notre table. Notre base va ressembler donc à ceci : base

Ajouter des Attributs « Data Annotation » :

Ce concept consiste en un attribut à ajouter soit sur les classes ou ses attributs afin de configurer le type, la longueur des champs, la clé primaire…

Ils existent plusieurs attributs « Data Annotations » dans l’espace de noms « System.ComponentModel.DataAnnotations » que nous pouvons utiliser dans l’approche Code First.

Parmi ces attributs, nous pouvons citer :

  • KeyAttribute
  •  RequiredAttribute
  • TimestampAttribute
  • LengthAttribute
  • StringLengthAttribute
  • ConcurrencyCheckAttribute …

Nous allons ajouter deux data annotations à un champ de notre classe « facture »

     public class Facture
    {
        public decimal IdFacture { get; set; }

        [Required]
        [StringLength(15)]
        public string NumFacture { get; set; }

En relançant l’application, nous allons obtenir un message d’erreur nous indiquant que le modèle de notre base a été changé depuis la création de la base et nous proposons ainsi d’utiliser code First Migration. Exception

Effectivement, une fois qu’Entity Framework a généré la base de donées, il n’est plus capable de la mettre à jour. Comme vous pouvez le voir dans le message d’erreur, ceci nous suggère d’utiliser Code First Migration pour mettre à jour la base de données déjà créée.

Le Concept du code First migration a été expliqué par Othman dans son article « Entity Framework Code First Migrations » que vous pouvez trouver ici :  https://blog.dcube.fr/blog/2014/10/23/entity-framework-code-first-migrations-2/ .

Nous pouvons néanmoins éviter d’avoir cette erreur en utilisant l’un des quatre modes de création « d’IDatabaseInitializer » de la base de données qui sont comme suit :

  • CreateDatabaseIfNotExists : C’est le mode de création par défaut. Comme son nom l’indique, ceci va créer la base si elle n’existe pas avec la configuration donnée, Dans ce cas, si vous changez le modèle et relancer l’application, une exception va être levée : Ce qui était notre cas.
  •  DropCreateDatabaseIfModelChanges : Si le modèle a été modifié, ce mode de création va supprimer la base existante et créer une autre, donc y’aura pas  l’exception levée précédemment si le modèle de données a été changé.
  • DropCreateDatabaseAlways : Comme son nom l’indique, ce mode de création supprime la base existante à chaque fois vous lancez l’application.
  • Custom DB Initializer : Vous pouvez aussi créer votre mode de création selon vos besoins en héritant de l’un des modes précédents.

Pour utiliser l’un des « IDatabaseInitializer » déjà existant, le code sera comme suit :

      public class FacturationContext : DbContext
        {
            public FacturationContext()
                : base("FacturationContext")
            {
                //cas 1
                Database.SetInitializer<FacturationContext>(new CreateDatabaseIfNotExists<FacturationContext>());
                //cas 2
                //Database.SetInitializer<FacturationContext>(new DropCreateDatabaseIfModelChanges<FacturationContext>());
                //cas 3
                //Database.SetInitializer<FacturationContext>(new DropCreateDatabaseAlways<FacturationContext>());
                //cas 4
                //Database.SetInitializer<FacturationContext>(new FacturationInitializer<FacturationContext>());
            }
            public DbSet<Facture> Factures { get; set; }
            public DbSet<Fournisseur> Fournisseurs { get; set; }

        }

Exemple de mode d’initialisation personnalisé qui hérite de DropCreateDatabaseAlways :

 public class FacturationInitializer : DropCreateDatabaseAlways<FacturationContext>
     {
         protected override void Seed(FacturationContext context)
         {
             base.Seed(context);
         }
     }

Nous pouvons aussi spécifier le mode de création dans le fichier de configuration comme suit :

  <appSettings>
    <add key="DatabaseInitializerForType GestionFactures.DAL.FacturationContext, GestionFactures.DAL"
     value="System.Data.Entity.DropCreateDatabaseAlways`1[[GestionFactures.DAL.FacturationContext, GestionFactures.DAL]], EntityFramework" />
    <add key="UserLogin" value="" />

Il est à noter que l’utilisation de ses modes d’initialisation de base de données d’Entity Framework mène à la perte des données de la base ce qui n’est généralement pas adapté à nos besoins surtout pour les environnements de production.

Par conséquent, il est fortement conseillé d’utiliser les commandes de code first Migration pour mettre à jour la base de données.

Nous pouvons forcer la désactivation des « Initializers » de DB. Ceci est faisable aussi bien par code ou dans le fichier de configuration comme suit :

    • Dans le code :
   public class FacturationContext : DbContext
    {   
        public FacturationContext()
            : base("FacturationContext") 
        {
            Database.SetInitializer<FacturationContext>(null);
        }
        public DbSet<Facture> Factures { get; set; }
        public DbSet<Fournisseur> Fournisseurs { get; set; }    
    }
    • Dans le fichier de configuration :
    <add key="DatabaseInitializerForType GestionFactures.DAL.SchoolDBContext, GestionFactures.DAL." value="Disabled" />

Conclusion :

Dans cet article, j’ai essayé de mettre en place l’approche Code First d’Entity Framework avec un exemple simple de mon projet.Nous sommes donc partis des simples classes qui décrivent notre modèle de données pour générer une base de données et la manipuler avec du PUR Code sans avoir le besoin de toucher à SQL management Studio! Ceci représente évidemment un point fort de cette méthodologie.

2 Commentaires

  1. Jean

    Merci pour ces explications.
    Serait-il possible d’avoir la solution complète (tous les projets) ?

    Réponse
  2. Adams

    Bonjour,

    Merci pour cet excellent tutoriel.

    Serait-il possible d’avoir le code source de ce projet.

    Merci d’avance.

    Réponse

Soumettre un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Découvrez nos autres articles

Aller au contenu principal