web-dev-qa-db-fra.com

Migrations Entity Framework 5: configuration d'une migration initiale et d'une graine unique de la base de données

J'ai une application MVC4 que j'ai récemment mise à niveau vers Entity Framework 5 et j'essaie de déplacer notre base de données vers l'utilisation des migrations à partir du style de développement de la suppression et de la création de chaque exécution.

Voici ce que j'ai fait dans la fonction de démarrage de mon application.

protected void Application_Start()
{
    Database.SetInitializer(
        new MigrateDatabaseToLatestVersion< MyContext, Configuration >() );
    ...
}

J'ai exécuté la commande Enable-Migrations Sur mon projet de référentiels et j'ai pensé que cela créerait un fichier de migration initial, mais le seul fichier qu'il a créé était Configuration

Lorsque je supprime la base de données, elle la crée comme prévu via le code en premier et initialise la base de données à partir du fichier de configuration. Dans le fichier de configuration, j'ai changé toutes les fonctions Add() en AddOrUpdate()

Cependant, il exécute la fonction de graine dans mon fichier Configuration à chaque démarrage du site Web et duplique toutes les données de graine encore et encore.

J'imaginais que cela créerait un fichier initial migration Comme le blog que j'ai lu le suggérait et je pourrais y mettre les données de départ, mais ce n'est pas le cas

Quelqu'un peut-il expliquer comment je devrais configurer DB dans le code pour qu'il ne démarre qu'une seule fois?


LIEN: l'article de blog sur les migrations que j'ai suivi


Bien que cela soit assez intéressant pour utiliser EF migrate.exe, je suis depuis passé à l'utilisation de roundhouse pour exécuter les migrations. J'utilise toujours EF pour échafauder mes migrations en fonction des modèles, mais j'ai écrit une petite application console pour écrire les migrations dans des fichiers SQL. J'utilise ensuite Roundhouse pour effectuer les migrations elles-mêmes via mes scripts de construction de râteau. Il y a un peu plus de processus impliqué, mais il est beaucoup plus stable que d'utiliser EF pour effectuer les migrations à la volée au démarrage de l'application.

28
Neil

Cela s'est avéré être un article populaire, donc je l'ai mis à jour à la lumière des commentaires des autres. La principale chose à savoir est que la méthode Seed dans la classe Configuration est exécutée CHAQUE fois que l'application démarre, ce qui n'est pas ce que le commentaire dans la méthode de modèle implique. Voir la réponse de quelqu'un à Microsoft à ce post pourquoi c'est - merci à Jason Learmouth de l'avoir trouvé.

Si vous, comme moi, souhaitez uniquement exécuter les mises à jour de la base de données s'il y a des migrations en attente, vous devez faire un peu plus de travail. Vous pouvez le savoir s'il y a des migrations en attente en appelant migrator.GetPendingMigrations (), mais vous devez le faire dans le ctor car la liste des migrations en attente est effacée avant Seed est appelée. Le code pour implémenter cela, qui va dans la classe Migrations.Configuration est le suivant:

internal sealed class Configuration : DbMigrationsConfiguration<YourDbContext>
{ 
    private readonly bool _pendingMigrations;

    public Configuration()
    {
        // If you want automatic migrations the uncomment the line below.
        //AutomaticMigrationsEnabled = true;
        var migrator = new DbMigrator(this);
        _pendingMigrations = migrator.GetPendingMigrations().Any();
    }

    protected override void Seed(MyDbContext context)
    {
        //Microsoft comment says "This method will be called after migrating to the latest version."
        //However my testing shows that it is called every time the software starts

        //Exit if there aren't any pending migrations
        if (!_pendingMigrations) return;

        //else run your code to seed the database, e.g.
        context.Foos.AddOrUpdate( new Foo { bar = true});
    }
}

Je dois souligner que certaines personnes ont suggéré de mettre le code de départ dans le code de migration "up" réel. Cela fonctionne, mais signifie que vous devez vous rappeler de mettre le code de départ dans chaque nouvelle migration et c'est assez difficile à retenir pour que je ne le fasse pas. Cependant, si votre graine change à chaque migration, cela pourrait être la bonne façon de procéder.

39
Jon P Smith

Vous pouvez ajouter une migration manuellement et la remplir avec le code d'amorçage que vous souhaitez? Dans la console du gestionnaire de packages, exécutez:

Add-Migration [Name]

Vous pouvez ensuite modifier ce fichier qui est créé pour vous dans votre dossier de migrations.

Dans mon projet, je fais en fait de l'ensemencement comme Richard mais dans la méthode Seed de la configuration du contexte. Je n'ai vraiment aucune préférence. Mais les migrations devraient être plus efficaces dans la mesure où l'application n'a pas besoin de vérifier si les lignes existent dans la base de données au démarrage de l'application. Il suffit de vérifier si la migration a été exécutée, ce qui devrait être plus rapide.

internal sealed class Configuration : DbMigrationsConfiguration<MyContext>
{
    public Configuration()
    {
        // If you want automatic migrations as well uncomment below.
        // You can use both manual and automatic at the same time, but I don't recommend it.
        //AutomaticMigrationsEnabled = true;
        //AutomaticMigrationDataLossAllowed = true;
    }

    protected override void Seed(MyContext 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.

        context.FontFamilies.AddOrUpdate(
            f => f.Id,
            new FontFamily { Id = 1, PcName = "Arial" },
            new FontFamily { Id = 2, PcName = "Times New Roman" },
        });

J'utilise ceci dans Global.asax:

public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        // Any migrations that haven't been applied before will
        // automatically be applied on Application Pool restart

        Database.SetInitializer<MyContext>(
            new MigrateDatabaseToLatestVersion<MyContext,
            MyApp.Migrations.Configuration>()
        );
    }
}
6
oldwizard

C'est quelque chose que je me suis aussi demandé dans le passé. J'ai certaines tables dans ma base de données qui sont remplies dans mon événement Seed, et maintenant je vérifie simplement si l'une d'entre elles est vide dans la méthode Seed . S'il y a des lignes, la méthode Seed ne s'exécute pas. Pas infaillible, mais fait l'affaire.

2
Richard

La réponse à this SO question explique pourquoi Seed s'exécute à chaque exécution de l'application).

J'utilise la méthode Jon Smiths, mais j'ai mis la vérification des instructions de migrations en attente dans un bloc #if comme ceci:

#if (!DEBUG)
            if (!_pendingMigrations) return;
#endif

De cette façon, lorsque je débogue la méthode Seed s'exécute toujours pour repeupler mes données de départ - utile lorsque je supprime pendant les tests, etc., mais je ne reçois pas la perf perf lors de la version.

2
Jason Learmouth