web-dev-qa-db-fra.com

Meilleure façon d'initier progressivement des données dans Entity Framework 4.3

J'utilise Entity Framework 4.3 sur une base de données existante et j'ai quelques scénarios à essayer.

Premièrement, si je supprime ma base de données, je voudrais que EF recrée si à partir de zéro - j'ai utilisé avec succès un initialiseur de base de données CreateDatabaseIfNotExists pour cela.

Deuxièmement, si je mets à jour mon modèle et que la base de données existe déjà, j'aimerais que la base de données soit mise à jour automatiquement - j'ai utilisé avec succès Entity Framework 4.3 Migrations pour cela.

Voici donc ma question. Supposons que j'ajoute une nouvelle table à mon modèle qui nécessite des données de référence, quelle est la meilleure façon de s'assurer que ces données sont créées à la fois lorsque l'intialiseur de base de données s'exécute et également lorsque la migration s'exécute. Mon souhait est que les données soient créées lorsque je crée la base de données à partir de zéro et également lorsque la base de données est mise à jour à la suite d'une migration en cours.

Dans certains exemples de migrations EF, j'ai vu des gens utiliser la fonction SQL () dans la méthode UP de la migration pour créer des données de départ, mais si possible, je préfère utiliser le contexte pour créer les données de départ (comme vous le voyez dans la plupart des exemples d'initialisation de base de données) car il me semble étrange que vous utilisiez du sql pur lorsque toute l'idée d'EF résume cela. J'ai essayé d'utiliser le contexte dans la méthode UP mais pour une raison quelconque, il ne pensait pas qu'une table créée lors de la migration existait lorsque j'ai essayé d'ajouter les données de départ directement sous l'appel pour créer la table.

Toute sagesse grandement appréciée.

41
Jim Culverwell

Si vous souhaitez utiliser des entités pour amorcer des données, vous devez utiliser la méthode Seed dans votre configuration de migrations. Si vous générez un nouveau projet Enable-Migrations vous obtiendrez cette classe de configuration:

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

    protected override void Seed(CFMigrationsWithNoMagic.BlogContext context)
    {
        //  This method will be called after migrating to the latest version.

        //  You can use the DbSet<T>.AddOrUpdate() helper extension method 
        //  to avoid creating duplicate seed data. E.g.
        //
        //    context.People.AddOrUpdate(
        //      p => p.FullName,
        //      new Person { FullName = "Andrew Peters" },
        //      new Person { FullName = "Brice Lambson" },
        //      new Person { FullName = "Rowan Miller" }
        //    );
        //
    }
}

La façon dont les données de semences de migrations ne sont pas très efficaces car elles sont censées être utilisées pour certains semis très basiques. Chaque mise à jour vers une nouvelle version passera par l'ensemble complet et tentera de mettre à jour les données existantes ou d'insérer de nouvelles données. Si vous n'utilisez pas la méthode d'extension AddOrUpdate, vous devez manuellement vous assurer que les données sont prédéfinies dans la base de données uniquement si elles ne sont pas encore présentes.

Si vous voulez un moyen efficace pour l'ensemencement parce que vous devez semer beaucoup de données, vous obtiendrez de meilleurs résultats avec des éléments communs:

public partial class SomeMigration : DbMigration
{
    public override void Up()
    {
        ...
        Sql("UPDATE ...");
        Sql("INSERT ...");
    }

    public override void Down()
    {
        ...
    }
}
55
Ladislav Mrnka

Je ne recommanderais pas d'utiliser les appels Sql() dans votre méthode Up() parce que (IMO) c'est vraiment destiné au code de migration réel pour lequel il n'y a pas de fonction intégrée, plutôt que le code de départ .

J'aime penser aux données de départ comme quelque chose qui pourrait changer à l'avenir (même si mon schéma ne change pas), donc j'écris simplement des vérifications "défensives" autour de toutes mes insertions dans la fonction de départ pour m'assurer que l'opération ne s'est pas déclenchée précédemment.

Considérez un scénario où vous avez une table "Types" qui commence avec 3 entrées, mais ensuite vous en ajoutez une 4ème. Vous ne devriez pas avoir besoin d'une "migration" pour résoudre ce problème.

L'utilisation de Seed() vous donne également un contexte complet avec lequel travailler, ce qui est beaucoup plus agréable que l'utilisation des chaînes SQL simples dans la méthode Sql() que Ladislav a démontré.

Gardez également à l'esprit que l'avantage d'utiliser les méthodes EF intégrées pour le code de migration et le code source est que vos opérations de base de données restent neutres sur la plate-forme. Cela signifie que vos modifications de schéma et vos requêtes peuvent être exécutées sur Oracle, Postgre, etc. Si vous écrivez du SQL brut réel, vous risquez de vous enfermer inutilement.

Vous pourriez être moins préoccupé par cela, car 90% des personnes utilisant EF ne toucheront que SQL Server, mais je le lance juste pour vous donner une perspective différente sur la solution.

32
Roger