32 votes

Update-Database échoue en raison de modifications en attente, mais Add-Migration crée une migration en double

Je travaille avec Entity Framework 5.0 Code First Migrations, et j'ai un problème avec l'exécution de Update-Database. Il est indiqué qu'il y a des modifications de modèle en attente, mais la base devrait être à jour.

Add-Migration SomeMigrationName

et il crée un fichier... cependant, il crée un fichier qui est essentiellement le même, une duplication d'une migration antérieure (si j'essaie de mettre à jour la base de données à nouveau sur ce fichier, il échoue avec des problèmes liés à la tentative de déposer une contrainte inexistante). De plus, j'ai pu confirmer que la migration "originale" a été exécutée en me basant à la fois sur le modèle de données de la BD et sur la présence d'un enregistrement dans la table __MigrationHistory !

Si je supprime toute la base de données, et que je relance toutes les migrations, automatiquement ou manuellement, j'ai le même problème.

Le fichier de migration "original" que j'avais est le suivant :

public partial class RenameLinkColumns : DbMigration
{
    public override void Up()
    {
        DropForeignKey("dbo.Listing", "OfferedByUserId", "dbo.User");
        DropIndex("dbo.Listing", new[] { "OfferedByUserId" });
        AddColumn("dbo.Listing", "ListedByUserId", c => c.Int(nullable: false));
        AddForeignKey("dbo.Listing", "ListedByUserId", "dbo.User", "UserId", cascadeDelete: true);
        CreateIndex("dbo.Listing", "ListedByUserId");
        DropColumn("dbo.Listing", "OfferedByUserId");
    }

    public override void Down()
    {
        AddColumn("dbo.Listing", "OfferedByUserId", c => c.Int(nullable: false));
        DropIndex("dbo.Listing", new[] { "ListedByUserId" });
        DropForeignKey("dbo.Listing", "ListedByUserId", "dbo.User");
        DropColumn("dbo.Listing", "ListedByUserId");
        CreateIndex("dbo.Listing", "OfferedByUserId");
        AddForeignKey("dbo.Listing", "OfferedByUserId", "dbo.User", "UserId", cascadeDelete: true);
    }
}

Lorsque j'ai relancé l'ajout de migration, les méthodes Up/Down de ce fichier sont exactement les mêmes que celles-ci.

Je suis assez impressionné par le fait que les migrations ont été correctement capables de détecter que j'avais renommé une colonne ForeignKey ; mais est-ce que c'est ce qui cause l'étranglement ?

Il semble qu'il existe un moyen de contourner le problème : J'ai supprimé la base de données et tous les fichiers de migration, et créé une nouvelle migration "initiale", mais je préférerais ne pas le faire si possible.

Mise à jour : C'était no la dernière migration qui a causé ce problème, mais le problème a commencé après une fusion (je travaille seul, mais je simule un travail d'équipe sur des branches pour en apprendre davantage sur git aussi), et en essayant de mettre la base de données en phase avec la fusion. Ce problème pourrait-il provenir du fait de placer les migrations dans un ordre particulier après une fusion - bien qu'il ait été noté, les migrations a fait fonctionnent comme prévu dans l'ordre où ils ont été exécutés lorsque je leur ai donné une BD vide.

De plus, cette migration originale nécessitait un ajustement manuel lorsque les tables contenaient des données, car les données devaient être copiées de l'ancienne colonne vers la nouvelle. Cependant, j'ai testé ce fichier avec et sans mes modifications manuelles dans ce fichier, et j'ai toujours rencontré le comportement indiqué.

36voto

Mike the Tike Points 432

Cette réponse explique pourquoi cela se produit . Pour le résoudre, j'appelle add-migration et le nommer MERGE puis supprimer tout code de migration dupliqué qui a déjà eu lieu. Il s'agit juste de mettre à jour l'instantané du modèle pour refléter le modèle fusionné.

Exemple :

public partial class MERGE : DbMigration
{
    public override void Up()
    {
        // Intentionally left blank.

        // This may seem like a hack, but it is necessary when using source control.
        // When a migration is created via add-migration, EF creates 
        // an .edmx file from the current code first classes. It compares this .edmx to the .edmx stored in the last migration before this, 
        // which I'll call it's parent migration. The edmx snapshots are gzipped and stored in base64 in the resource files (.resx) if you 
        // want to see them. EF uses the difference between these two snapshots to determine what needs to be migrated.

        // When using source control it will happen that two users add entities to the model independently. The generated edmx snapshots will 
        // only have the changes that they have made. When they merge in source control, they will end up with this:

        // Migration                        |  Snapshot Contents
        // -------------------------------- | ----------------
        // 20150101_Parent Migration        |  A
        // 20150102_Developer 1's Migration |  A + Change 1
        // 20150103_Developer 2's Migration |  A + Change 2

        // So calling add-migration will create the current snapshot edmx from the Code First model and compare it to the 
        // the latest migration's snapshot, which is A + Change 2, and see that Change 1 is missing. That is why it 
        // creates a duplicate migration. We know that the migrations have already been applied, so the only thing that this 
        // migration will do is update the current snapshot .edmx so that later migrations work fine.
    }

    public override void Down()
    {

    }
}

0 votes

Bonjour Mike Je ne suis pas en mesure de vérifier votre réponse car mon environnement de développement n'existe plus depuis longtemps, mais j'accepte la réponse car vous avez fourni un raisonnement convaincant (et un beau commentaire sur la migration ci-dessus :)

2 votes

Merci pour ces explications. J'avais déjà travaillé sur cette solution, mais je n'étais pas satisfait de la vérifier sans comprendre pourquoi elle fonctionnait.

2 votes

Cela fonctionne mais c'est horrible, je n'ai pas envie d'avoir une migration qui ne fait rien à chaque fois que j'ai un changement stupide.

4voto

Lars Points 65

Je vois cela tout le temps aussi. Je ne sais pas pourquoi, j'aimerais le savoir, mais ma solution est de faire une add-migration qui créera un double. Maintenant, ce duplicata s'ouvre dans l'éditeur et je le modifie, de sorte que les méthodes Up et Down soient vides. Le résultat est donc un fichier de migration qui ne fait rien ! VS est content et vous pouvez faire la mise à jour de la base de données sans erreur (jusqu'à la prochaine fois).

J'espère que cela vous aidera :)

4voto

tom_redox Points 43

En guise de réponse générale à cette partie de la question :

(Je travaille seul, mais je simule un travail d'équipe sur les branches pour apprendre pour en savoir plus sur git), et j'essaie de mettre la base de données en phase avec la fusion. Cela pourrait-il provenir du fait de placer les migrations dans un certain dans un ordre particulier après une fusion

Oui, cela peut être le cas, il est assez facile pour EF de s'embrouiller après une fusion, mais il est possible de le résoudre. La clé est de comprendre pourquoi il s'est embrouillé en premier lieu :

Que se passe-t-il lorsque des migrations sont fusionnées à partir de branches ?

La raison pour laquelle EF est confus est que EF stocke la forme actuelle de la base de données dans le fichier de migration réel, c'est la valeur 'Target' dans le fichier resx qui se trouve sous chaque migration, par ex.

migration file in solution explorer showing child .resx file

migration resx file showing the Target

Imaginez que vous avez deux branches :

  • Branche 1 : Vous ajoutez le champ 'URL' à la table Blog. Le champ Target contient maintenant une description de la base de données avec ce champ supplémentaire.
  • Branche 2 : Vous ajoutez une nouvelle table 'Links'. Encore une fois, la description de la base de données dans le champ Cible a maintenant cette table supplémentaire, mais elle n'a pas le champ URL car il a été ajouté dans une autre branche.

Si vous fusionnez maintenant ces deux branches sur la branche principale et essayez ensuite d'exécuter des migrations, vous risquez d'obtenir le redoutable message suivant

Impossible de mettre à jour la base de données pour qu'elle corresponde au modèle actuel parce qu'il y a des des modifications en attente et la migration automatique est désactivée...

Ce message d'erreur est vraiment trompeur et peu utile, mais la cause de l'erreur est en fait assez simple à comprendre :

Pourquoi les branches multiples déroutent-elles EF ?

Lorsque les deux branches ont été fusionnées sur Master, celle d'entre elles qui est maintenant la dernière migration (selon les dates au début du nom du fichier) est considérée par EF comme ayant le véritable état actuel de la base de données dans le champ Target de cette migration.

Cependant, comme nous l'avons vu plus haut, les branches 1 et 2 ont toutes deux une vision différente de l'état réel de la base de données (l'une pense qu'il y a un nouveau champ URL, l'autre pense qu'il y a un nouveau champ liens), et de façon peu utile, elles sont maintenant les deux faux parce que la base de données a ces deux champs maintenant.

Le message d'erreur se produit parce que EF calcule l'état attendu de la base de données à partir des étapes réelles des migrations, le compare à la cible et constate qu'ils sont différents.

Comment le réparer

La solution à tout cela est de forcer EF à recalculer l'état de la base de données en se basant sur toutes les migrations en cours dans le projet, puis de mettre à jour la valeur de la cible pour qu'elle inclue les changements effectués le jour de la migration. tous les migrations.

La façon la plus simple de le faire est d'ajouter une migration "vierge", en utilisant la commande :

Add-Migration <pick_a_name> –IgnoreChanges

L'autre approche consiste à écraser la valeur Target dans la migration finale.

Consultez le manuel

Tout ce qui précède n'est qu'un bref aperçu du superbe guide pour comprendre les migrations en général et aussi dans un environnement d'équipe que l'on peut trouver dans :

Les migrations Code First de Microsoft dans les environnements d'équipe

Ce document devrait être mentionné dans chaque message d'erreur EF, car il donne un sens à tant de problèmes EF quotidiens.

1 votes

Merci pour cette belle explication ! @tomRedox

0voto

Vic Points 372

Je viens de rencontrer le même problème.

Une fois la migration créée. J'ai essayé de mettre à jour la base de données et j'ai reçu le message suivant :

Impossible de mettre à jour la base de données pour qu'elle corresponde au modèle actuel parce qu'il y a des des modifications en attente et la migration automatique est désactivée. Soit vous écrivez les modèles en attente dans une migration basée sur le code ou activez la migration automatique. Définissez DbMigrationsConfiguration.AutomaticMigrationsEnabled comme suit true pour activer la migration automatique. Vous pouvez utiliser la commande Add-Migration pour écrire les modifications de modèle en attente dans une migration basée sur le code.

Ensuite, j'ai généré à nouveau la migration mais elle a été dupliquée.

Le problème a été résolu lorsque j'ai Construire le projet après avoir créé la migration. Ensuite, Update-Database script trouve la méthode de migration et cela fonctionne. Au moins pour mon cas.

0voto

Christian Müller Points 487

Ce n'est pas la solution, cela aiderait toujours. Je recommande également toutes les réponses.

J'ai eu le même problème et la suggestion de Mike ne s'applique pas dans mon cas. J'ai découvert pourquoi ...

El DLL avec les migrations faisait partie de un déploiement (sharepoint) et aussi dans le CAG ( C:\Windows\Microsoft.NET\assembly\GAC_MSIL ).

J'ai supprimé la DLL du GAC et redémarré Visual Studio.

Maintenant "update-database" a pris la bonne DLL et la migration a bien fonctionné.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X