I. Introduction

Pendant le développement ou le cycle de vie d'une application, le développeur est très souvent confronté à des situations pouvant entrainer des modifications de son modèle objet ainsi que de sa base de données. Avec Entity Framework, cette tâche peut s'avérer assez complexe, voire frustrante pour certains.

Si vous voulez par exemple apporter une modification (ajout d'une colonne) à une table de votre base de données, si vous vous limitez à définir uniquement la nouvelle colonne dans votre modèle, lors de l'exécution de l'application vous allez obtenir un message d'erreur puisque le modèle ne correspond plus aux objets de la BD.

Il était cependant possible d'ajouter quelques lignes de code dans le fichier Global.asax, permettant lors de la modification du modèle, de détruire et recréer automatiquement la base de données lors de la prochaine exécution de l'application, avec comme conséquence directe la perte des données. Ce qui est inacceptable pour une base de données déjà en production.

Heureusement, Entity Framework 4.3 élimine ces contraintes avec la nouvelle fonctionnalité Code First Migrations, qui permet d'appliquer avec souplesse les modifications du modèle sur la base de données, en réduisant les risques de pertes de données.

II. Prérequis

Il est important que vous ayez des connaissances de base sur la programmation .NET, le langage C# et l'ORM Entity Framework.

Pour l'exemple qui sera décrit tout au long de cet article, nous avons utilisé Visual Web Developer 2010 Service Pack 1, ASP.NET MVC 4 Beta et Entity Framework 4.3.

III. Utilisation de Code First Migrations

Créez une nouvelle application ASP.NET MVC 4 " DataMigration " en utilisant le modèle Internet Application. Avec NuGet, vous allez faire une mise à jour d'Entity Framework vers la dernière version de la bibliothèque, qui ajoute les fonctionnalités de migrations des bases de données.

Image non disponible

Vous pouvez également utiliser la console NuGet Manager pour effectuer la mise à jour d'Entity Framework ou installer celui-ci.

Pour plus de détails sur NuGet, voir le tutoriel : Présentation du gestionnaire de packages .NET NuGet.

Vous allez ajouter une nouvelle classe " Customer " dans le modèle. Le code pour cette classe est le suivant :

 
Sélectionnez

public class Customer
    {
        public short Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
       

Vous allez par la suite créer un nouveau contrôleur en utilisant Entity Framework pour les actions CRUD.

Dans les options de scaffolding, vous devez sélectionner la classe Customer. Dans la zone Data context class, vous allez utiliser " New data context…>. Renseignez le nom du Data Context " DatabaseMigrationContext ". Validez ensuite sur OK et cliquez enfin sur Ajouter.

Image non disponible

Un fichier DataBaseMigrationContext.cs sera automatiquement ajouté à votre application.

Exécutez celle-ci afin que Code First utilise le modèle pour créer la base de données qui sera automatiquement ajoutée à votre instance SQL Server locale.

Image non disponible

III-A. Activation de la migration

Supposons que vous voulez ajouter une propriété Email dans votre modèle. Vous allez pour ce faire modifier celui-ci en ajoutant cette nouvelle propriété :

 
Sélectionnez

public class Customer
    {
        public short Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string EMail { get; set; }

    }

Si vous exécutez votre application ainsi, vous allez obtenir le message d'erreur suivant :

Image non disponible

Comme l'exception le signale, il est temps pour vous d'utiliser Code First Migrations pour procéder à la mise à jour de votre base de données.

La première étape est d'activer la migration pour votre Context, en utilisant la console du gestionnaire de packages (menu Outils/Gestionnaire de package/Package Manager Console de Visual Studio). Vous allez exécuter la commande suivante : Enable-Migrations.

Image non disponible

L'activation de la migration crée un nouveau dossier Migrations dans votre application contenant un fichier Configurations.cs, permettant de définir le comportement de la migration pour le DbContext utilisé.

 
Sélectionnez


namespace DatabaseMigration.Migrations
{
    using System;
    using System.Data.Entity;
    using System.Data.Entity.Migrations;
    using System.Linq;

    internal sealed class Configuration : DbMigrationsConfiguration<DatabaseMigration.DatabaseMigrationContext>
    {
        public Configuration()
        {
            AutomaticMigrationsEnabled = false;
        }

        protected override void Seed(DatabaseMigration.DatabaseMigrationContext context)
        {
            
        }
    }
}

Si vous avez défini plusieurs "DbContext " dans votre application, il suffira de modifier DbMigrationsConfiguration(T), en remplaçant T par le nom du DbContext que vous voulez utiliser.

Deux modes peuvent être utilisés pour mettre à jour la base de données en utilisant Database Migrations. Vous pouvez décider d'utiliser la migration basée sur le code ou la migration automatique. Il suffit de faire passer AutomaticMigrationsEnabled à False pour le premier cas, et True pour le second.

Un autre fichier est également créé. Ce fichier contient une procédure Up() qui contient des instructions pour créer la table avec la définition initiale de celle-ci, et une autre procédure Down() pour supprimer la table.

Ce fichier pourra être utilisé pour rétablir l'état original de la base de données.

 
Sélectionnez

public partial class InitialCreate : DbMigration
    {
        public override void Up()
        {
            CreateTable(
                "Customers",
                c => new
                    {
                        Id = c.Short(nullable: false, identity: true),
                        FirstName = c.String(),
                        LastName = c.String(),
                    })
                .PrimaryKey(t => t.Id);
            
        }
        
        public override void Down()
        {
            DropTable("Customers");
        }
    }

Il est important de savoir comment fonctionnent les procédures Up() et Down() qui sont générées automatiquement, et à quoi correspond l'action effectuée par chacune d'elles (mise à jour ou annulation d'une mise à jour). La procédure Up() contient les lignes de code permettant de faire une mise à jour de la base de données (ajout d'une colonne, ajout d'une table, modification d'une colonne, etc.). La procédure Down() contient les lignes de code permettant de revenir en arrière pour une mise à jour dont le code est défini dans la procédure Up() (supprimer la colonne créée, supprimer la table créée, etc.).

IV. Utilisation de la migration automatique

Par défaut, la propriété AutomaticMigrationsEnabled est définie à False. Pour utiliser la migration automatique, vous devez affecter la valeur True à cette propriété.

Il également possible de définir cette propriété à True lors de l'activation de la migration. Pour cela, vous devez ajouter le paramètre EnableAutomaticMigrations à la commande permettant d'activer la migration :

 
Sélectionnez

Enable-Migrations -EnableAutomaticMigrations

Pour appliquer la modification que vous avez apportée à la classe Customer à votre base de données, vous allez utiliser la commande 'Update-Database' dans la console du gestionnaire de packages NuGet.

Vous pouvez observer les modifications qui ont été apportées à la base de données dans l'explorateur de serveur au sein de SQL Server Management Studio :

Image non disponible

Passons à un second exemple. Vous souhaitez ajouter une nouvelle table Product à votre base de données.

Pour cela, vous allez dans un premier temps ajouter une nouvelle classe Product dans le dossier modèle ayant la définition suivante :

 
Sélectionnez

public class Product
    {
        public short Id { get; set; }
        public string Name { get; set; }
    }

N'oubliez surtout pas de référencer la classe entité Product que vous venez de créer dans votre DbContext.

 
Sélectionnez

public DbSet<Product> Products { get; set; }

Vous pouvez maintenant procéder à la création de la table "Products" en utilisant la commande " Update-Database ", à la différence que vous allez ajouter le paramètre " -Verbose " afin que la commande SQL utilisée soit affichée dans la console NuGet.

Image non disponible

Un petit coup d'œil sur la base de données et vous allez vous rendre compte que la table Products a été ajoutée à celle-ci.

IV-A. Configuration de la migration automatique

Jusqu'ici, vous vous rendez compte que vous appliquez vous-même les changements apportés au modèle à la base de données en utilisant la commande 'Update-Database'. En principe, lors de l'utilisation de la migration automatique, Entity Framework Code First doit être capable de détecter automatiquement les changements qui ont été apportés au modèle et les appliquer à la base de données.

Pour cela, il suffit d'ajouter la procédure OnModelCreating à votre DbContext (DataBaseMigrationContext.cs) :

 
Sélectionnez

protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            Database.SetInitializer(new MigrateDatabaseToLatestVersion<DatabaseMigrationContext, Configuration>());
        }

Vous devez également modifier le fichier Configuration.cs pour prendre en compte les migrations pouvant entrainer des pertes de données, en ajoutant la propriété AutomaticMigrationDataLossAllowed :

 
Sélectionnez

public Configuration()
        {
            AutomaticMigrationsEnabled = true;
            AutomaticMigrationDataLossAllowed = true;
        }

Ainsi, toute modification (nouvelle entité, ajout d'une propriété, etc.) du modèle sera automatiquement appliquée à la base de données lors de l'exécution de votre application.

IV-B. Inconvénients de la migration automatique

La migration automatique permet d'apporter assez rapidement et simplement des mises à jour à une base de données en utilisant Entity Framework Code First. Cette option, bien que très pratique, présente cependant quelques inconvénients et il n'est de ce fait pas conseillé d'utiliser ce mode pour une base de données critique ou en environnement de production.

Néanmoins, vous pouvez limiter ces risques en environnement de production en désactivant l'autorisation des mises à jour pouvant entrainer des pertes de données.

 
Sélectionnez

AutomaticMigrationDataLossAllowed = false;

V. Migration basée sur le code

La migration basée sur le code est un mode qui offre beaucoup plus de possibilités au développeur pour les mises à jour de la base de données, ainsi qu'une plus grande visibilité sur les opérations qui sont effectuées sur sa BD.

Ce mode utilise des fichiers de code générés par Entity Framework qui contiennent des instructions LinQ et SQL des modifications qui seront apportées à la base de données à partir des nouvelles définitions du modèle.

V-A. Génération des fichiers de migrations

Après avoir activé la migration, vous devez utiliser la commande Add-Migration suivie du nom de la migration pour générer le fichier de code des modifications qui doivent être apportées à la base de données.

Ajoutez par exemple un nouvel attribut Rating à la classe Product.

 
Sélectionnez

public class Product
    {
        public short Id { get; set; }
        public string Name { get; set; }
        public int Rating { get; set; }
    }

Exécutez par la suite la commande Add-Migration AddColumn dans la console NuGet. Le paramètre AddColumn représente le nom de la migration.

Image non disponible

Un nouveau fichier AddColumn.cs sera automatiquement ajouté au dossier Migrations, avec un préfixe timestamp permettant à Entity Framework de savoir quels fichiers doivent être exécutés, et dans quel ordre :

Image non disponible

Ce fichier contient par défaut le code suivant :

 
Sélectionnez

namespace DatabaseMigration.Migrations
{
    using System.Data.Entity.Migrations;
    
    public partial class AddColumn : DbMigration
    {
        public override void Up()
        {
            AddColumn("Products", "Rating", c => c.Int(nullable: false));
        }
        
        public override void Down()
        {
            DropColumn("Products", "Rating");
        }
    }
}

Vous pouvez apporter des modifications à ce fichier en ajoutant ou en supprimant des colonnes, ou en y insérant même des instructions SQL.

 
Sélectionnez

public override void Up()
        {

            AddColumn("Products", "Description", c => c.String());

            Sql("UPDATE Products SET Description = LEFT(Content, 100) WHERE Description IS NULL");
        }
        

Lorsque tout est OK, vous pouvez utiliser la commande Update-Database pour appliquer la migration sur votre base de données.

V-B. Exécuter un fichier de migration spécifique

Pour exécuter un fichier de migration spécifique, vous pouvez utiliser la commande 'Update-Database -TargetMigration:"NomDeLaMigration"'

 
Sélectionnez

PM> Update-Database -TargetMigration:"AddColumn"
Image non disponible

Si la migration a déjà été appliquée, la méthode Down() sera exécutée dans ce cas pour annuler les modifications qui ont été apportées à la base de données.

Pour revenir à un état antérieur, vous pouvez également utiliser la commande :

 
Sélectionnez

PM > Update-Database -TargetMigration:$InitialDatabase

VI. Utilité de la table _MigrationHistory dans ma base de données

En observant les objets de votre base de données, vous vous rendez compte qu'une table système _MigrationHistory a été ajoutée à la BD.

Image non disponible

La table _MigrationHistory est créée pour stocker les métadonnées sur les migrations qui ont été appliquées à la base de données. Cette table est créée lorsque Entity Framework Code First procède à la création de la base de données ou lorsque les migrations sont activées.

Image non disponible

Elle sera utilisée par Entity Framework Code First pour connaitre l'état actuel de la migration qui a été appliquée à la base de données.

Cette table peut également s'avérer utile pour savoir les modifications qui ont été apportées à votre base de données.

VII. Conclusion

Au travers de cet article, vous avez découvert une des nouveautés les plus intéressantes d'Entity Framework 4.3. Désormais, les modifications de votre modèle pourront être aisément appliquées à votre base de données avec le moins d'effort possible.

VIII. Remerciements

Je tiens à remercier tomlev et ClaudeLELOUP pour leurs relectures et corrections.

IX. Liens