web-dev-qa-db-fra.com

Confusion sur EF Auto Migrations et l'amorçage - amorçage à chaque démarrage du programme

J'ai récemment changé une application en utilisant ce qui suit pour dev:

DropCreateDatabaseIfModelChanges<Context>


Pour utiliser:

public class MyDbMigrationsConfiguration: DbMigrationsConfiguration<GrsEntities>
{
    public MyDbMigrationsConfiguration()
    {
        AutomaticMigrationsEnabled = true;
        AutomaticMigrationDataLossAllowed = true;
    }
}


Dans mon contexte db j'ai:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    // Tell Code First to ignore PluralizingTableName convention
    // If you keep this convention then the generated tables will have pluralized names.
    modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();

    //set the initializer to migration
    Database.SetInitializer(new MigrateDatabaseToLatestVersion<GrsEntities, MigrationConfig>());
}

J'ai remplacé Seed (contexte) dans DbMigrationsConfiguration en utilisant l'extension AddOrUpdate où j'utilisais juste Add auparavant avec l'amorçage sur la base de données de dépôt (DropCreateDatabaseIfModelChanges).

Ma confusion est que la migration s'exécute à chaque démarrage de l'application, indépendamment de toute modification du DbContext. Chaque fois que je lance l'application (bibliothèque exécutée via un service), l'initialiseur fonctionne comme le fait la graine. Mon comportement attendu est de vérifier si une migration est nécessaire (vérifier en arrière-plan si le modèle correspond à la base de données physique), puis de mettre à jour toutes les tables/colonnes nouvelles/supprimées et d'exécuter la graine uniquement si quelque chose a changé.

Dans mes tests, les semences fonctionnent à chaque fois, ce qui est réalisable mais apparemment inefficace et n'était pas ce à quoi je m'attendais. Malheureusement, la documentation MSDN est assez limitée.

Suis-je complètement en train d'utiliser abusivement MigrateDatabaseToLatestVersion? Existe-t-il un moyen d'obtenir le comportement que j'attends (c'est-à-dire uniquement si le modèle est modifié) ou dois-je simplement modifier ma méthode de base pour espérer être exécuté à chaque lancement d'application?

42
shox

Le fait que la méthode Seed ne s'exécutait que lorsque la base de données a été modifiée était assez limitant pour les initialiseurs de base de données livrés dans EF 4.1. C'était limitant car parfois vous deviez mettre à jour les données de base sans changer la base de données, mais pour y arriver, vous avez dû artificiellement donner l'impression que la base de données avait changé.

Avec Migrations, l'utilisation de Seed est devenue un peu différente car on ne pouvait plus supposer que la base de données commençait à vide - c'est un peu le but des migrations après tout. Donc un Seed dans Migrations doit supposer que la base de données existe et peut déjà contenir des données, mais que les données peuvent avoir besoin d'être mises à jour pour prendre en compte les modifications apportées à la base de données pour les migrations. D'où l'utilisation de AddOrUpdate.

Nous avons donc maintenant une situation où Seed doit être écrit pour tenir compte des données existantes, ce qui signifie qu'il n'est vraiment pas nécessaire de perpétuer les limites de l'EF 4.1 Seed méthode telle que vous auriez à donner l'impression que la base de données a changé juste pour que Seed soit exécuté. D'où Seed s'exécute maintenant tous les la première fois que le contexte est utilisé pour la première fois dans le domaine de l'application. Cela ne devrait pas changer la façon dont Seed est implémenté car il doit de toute façon gérer le cas où les données sont déjà présentes.

Si cela cause des problèmes de performances parce que vous avez beaucoup de données Seed, il est généralement facile d'ajouter des vérifications dans la méthode Seed qui interroge la base de données pour déterminer combien de travail doit être fait avant de le faire.

61
Arthur Vickers

Je suis quelque peu d'accord avec Arthur Vickers réponse, cependant IMO Seed est pour DbMigrations et je ne veux pas que la méthode Seed soit tout vérifier à chaque fois, par exemple, si j'ai 4 migrations, je devrais tester d'une manière ou d'une autre quelles données doivent être amorcées et ce sera au moins 4 hits de base de données au moins. Au cas où vous voudriez toujours avoir le comportement de l'exécution Seed méthode uniquement lorsque les migrations sont appliquées, comme moi, je suis venu avec ma propre implémentation de la stratégie IDatabaseInitializer

public class CheckAndMigrateDatabaseToLatestVersion<TContext, TMigrationsConfiguration>
    : IDatabaseInitializer<TContext>
    where TContext : DbContext
    where TMigrationsConfiguration : DbMigrationsConfiguration<TContext>, new()
{
    public virtual void InitializeDatabase(TContext context)
    {
        var migratorBase = ((MigratorBase)new DbMigrator(Activator.CreateInstance<TMigrationsConfiguration>()));
        if (migratorBase.GetPendingMigrations().Any())
            migratorBase.Update();
    }
}
18
Guillermo Ruffino

Une autre option peut consister à charger une classe d'initialisation de base de données personnalisée au moment de l'exécution dans la méthode de départ. L'application de production pourrait alors charger un initialiseur factice, tandis que l'application de développement pourrait charger l'initialiseur réel. Vous pouvez utiliser Unity/MEF

    // Unity Dependency Injection Prop
    [Dependency]
    property IMyInitializer initializer;

    protected override Seed(YourContextClass context)
    {
       initializer.Seed(context);
    }

Quelque chose comme ca. Vous basculeriez ensuite les initialiseurs une fois que vous avez la configuration de la base de données dans Production, vers le mannequin, cela ne ferait rien.

2
Gary Gaughan-Smith