it-swarm.com.de

Migrationen in Entity Framework in einer kollaborativen Umgebung

Wir haben mehrere Entwickler, die an einem Projekt arbeiten, das Entity Framework 5.0 verwendet. Jeder Entwickler verwendet seine eigene lokale SQL 2012-Datenbank, damit er entwickeln und testen kann, ohne andere zu behindern.

Zunächst verwendeten wir eine Mischung aus automatischen Migrationen und codebasierten Migrationen. Das hat überhaupt nicht geklappt, also haben wir uns entschlossen, automatische Migrationen zu deaktivieren und nur Code-basierte zuzulassen. Ich sollte hinzufügen, dass wir erneut mit einer sauberen Datenbank ohne 'beschädigten' _MigrationsHistory von allen automatischen Migrationen gestartet sind.

Der Workflow ist nun also:

  1. Der Entwickler ändert sein Datenmodell
  2. Tut add-migration <Name> und wendet es mit update-database auf seine Datenbank an.
  3. Checkt den Datenmodellwechsel und die Migration nach Git ein.
  4. Ein anderer Entwickler zieht die Änderungen ab, empfängt sie und wendet sie auf seine Datenbank an.

Bisher hat das gut funktioniert. Bis heute war es jedoch in der Regel nur ich, der die Migrationen durchgeführt hat, und die anderen haben sie angewendet. Aber heute gab es Migrationen von drei Entwicklern. Ich habe gerade diese Migrationen durchgeführt und einen update-database durchgeführt, der gut funktioniert hat.

Ich hatte auch eine Änderung an meinem eigenen Datenmodell, also gab es am Ende des update-database eine Warnung, dass ich immer noch nicht auf dem neuesten Stand war, also tat ich add-migration <my migration>. Als die Migration jedoch gerüstet wurde, wurden mir die Änderungen aller Migrationen angezeigt, die ich bereits auf die Datenbank angewendet hatte. Also: Es wurde versucht, bereits gelöschte Spalten zu löschen, eine bereits vorhandene Tabelle zu erstellen usw.

Wie kann das sein? Ich ging davon aus, dass EF nur die Tabelle _MigrationsHistory überprüft und herausfindet, welche Migrationen noch nicht in der Tabelle vorhanden sind, und diese nacheinander anwendet, geordnet nach dem Zeitstempel, der Teil des Namens ist. Aber anscheinend nicht, denn selbst wenn ich meine eigenen Änderungen rückgängig mache und eine saubere Umgebung habe, beschwert es sich, dass meine Datenbank nicht mit dem Modell synchronisiert ist. Aber ich habe diese Änderungen einfach übernommen und in meine Datenbank übernommen. Es ist ist synchron. Ich kann die Migrationen, die ich gerade angewendet habe, auch in der Tabelle _MigrationsHistory sehen.

Das einzige, woran ich denken kann, ist, dass ich einem Datenmodell eine Eigenschaft hinzugefügt habe, die keine Datenbankänderung zur Folge hätte (Ich habe dem Datenmodell Y einen List<X> hinzugefügt, wobei X die vielen in der Eins-zu-viele-Beziehung sind. t führt zu einer Datenbankänderung, da X bereits einen Fremdschlüssel für Y hatte. Könnte es das sein? Wenn ja, ist das sehr fragil, da es keine Möglichkeit gibt, eine Migration hinzuzufügen, da keine Datenbankänderungen vorgenommen werden und ich nicht sicher bin, wie dies behoben werden kann.

Ich bin mir nicht sicher, wie ich damit umgehen soll, da ich natürlich einfach das Gerüst bearbeiten und alles entfernen kann, was bereits auf meine Datenbank angewendet wurde. Aber was dann? Ich checke es ein und dann erhält ein anderer Entwickler die gleiche Nachricht, dass seine Datenbank auch nach dem Anwenden meiner neuen Änderungen nicht auf dem neuesten Stand ist. Er baut seine eigenen Änderungen ein, bekommt das gleiche Unsinn-Gerüst, bearbeitet es, checkt es ein und dann das next Entwickler bekommt es. Es wird ein Teufelskreis, ähnlich dem, den wir hatten, als wir automatische Migrationen verwendeten, und ich dachte, wir hätten das behoben, indem wir nur auf Code umgestiegen sind. Ich kann es im Moment nicht glauben, das Richtige zu tun, und es ist ein Albtraum, mit so etwas zu arbeiten.

Was ich auch versucht habe, ist das Hinzufügen der Migrationen, die ich mit update-database -t:201211091112102_<migrationname> einzeln von meinen Mitarbeitern abgerufen habe, jedoch ohne Erfolg. Es gibt mir immer noch das falsche Gerüst.

Was haben wir hier falsch gemacht, oder ist EF einfach nicht für eine solche Zusammenarbeit geeignet?

UPDATE

Ich habe einen reproduzierbaren Testfall erstellt, der allerdings etwas langwierig ist, um dieses Multi-User/Multi-Database-Szenario zu simulieren.

https://github.com/JulianR/EfMigrationsTest/

Schritte zum Reproduzieren, wenn Sie das obige Projekt haben (diese Schritte sind auch im Code enthalten):

  1. add-migration Init
  2. update-Datenbank (auf Datenbank 'TestDb')
  3. Ändern Sie die Verbindungszeichenfolge so, dass sie auf TestDb1 zeigt
  4. update-Datenbank auf TestDb1
  5. Entkommentieren Sie die Eigenschaft Foo in der Klasse Test
  6. add-migration M1 zum Hinzufügen der Eigenschaft Foo zu TestDb1
  7. Kommentieren Sie Test.Foo erneut aus
  8. Ändern Sie die Verbindungszeichenfolge so, dass sie auf TestDb2 zeigt
  9. Schließen Sie die Migration M1 aus dem Projekt aus, damit sie nicht auf TestDb2 angewendet wird
  10. Eigenschaft auskommentieren Leiste bei Klassentest
  11. update-database zum Anwenden der Init-Migration auf TestDb2
  12. add-migration M2 zum Hinzufügen der Eigenschaftsleiste zu TestDb2
  13. Ändern Sie die Verbindungszeichenfolge so, dass sie erneut auf die ursprüngliche TestDb verweist
  14. Fügen Sie die Migration M1 erneut in das Projekt ein
  15. Entkommentieren Sie die Eigenschaft Foo in der Klasse Test
  16. Kommentiere die Eigenschaft SomeInt in der Klasse Test aus
  17. datenbank auf den neusten Stand bringen
  18. add-Migration M3
  19. update-database, erhalte einen Fehler, weil M3 versucht, die Spalte Foo zur Datenbank TestDb hinzuzufügen, die gerade erst durch die Migration M1 hinzugefügt wurde.

Das obige soll drei Benutzer simulieren, wobei Benutzer 1 in seiner Datenbank ist, die anderen beiden verwenden seine Initialisierung, um ihre Datenbank ebenfalls zu erstellen. Anschließend nehmen Benutzer 2 und Benutzer 3 ihre eigenen Änderungen am Datenmodell vor und fügen sie zusammen mit den zum Übernehmen der Änderungen erforderlichen Migrationen der Quellcodeverwaltung hinzu. Dann ruft Benutzer 1 die Änderungen von Benutzer 2 und 3 ab, während Benutzer 1 selbst eine Änderung an der Datenbank vorgenommen hat. Dann ruft Benutzer 1 update-database auf, um die Änderungen von Benutzer 2 und 3 zu übernehmen. Anschließend erstellt er eine eigene Migration, die dann fälschlicherweise eine Änderung von Benutzer 2 oder 3 zu der gerüsteten Migration hinzufügt, die bei der Anwendung auf die Datenbank von Benutzer 1 einen Fehler verursacht.

42
JulianR

Sie müssen Migrationskonflikte genauso manuell lösen, wie Sie Konflikte codieren würden. Wenn Sie eine Aktualisierung durchführen und neue Migrationen vorliegen, müssen Sie sicherstellen, dass die Metadaten hinter der letzten Migration mit dem aktuellen Modell übereinstimmen. Setzen Sie den Befehl Add-Migration erneut ab, um die Metadaten der Migration zu aktualisieren.

Beispielsweise sollten Sie vor Schritt 17 (Update-Database) in Ihrem Szenario den folgenden Befehl ausgeben

Add-Migration M2

Dadurch werden die Metadaten aktualisiert, um sie mit Ihrem aktuellen Modell zu synchronisieren. Wenn Sie nun versuchen, M3 hinzuzufügen, sollte dieses Feld leer sein, da Sie keine weiteren Modelländerungen vorgenommen haben.

4
bricelam

Sie müssen eine leere "Zusammenführungs" -Migration hinzufügen, die den Snapshot der letzten Migration in der RESX-Datei zurücksetzt. Verwenden Sie dazu den Schalter IgnoreChanges:

Add-Migration <migration name> -IgnoreChanges

Siehe hier für eine Erklärung

17
Dewey

Option 1: Fügen Sie eine leere "Zusammenführungs" -Migration hinzu

  1. Stellen Sie sicher, dass alle ausstehenden Modelländerungen in Ihrer lokalen Codebasis in eine Migration geschrieben wurden. Dieser Schritt stellt sicher, dass Sie keine legitimen Änderungen verpassen, wenn die leere Migration generiert wird.
  2. Mit der Quellcodeverwaltung synchronisieren.
  3. Führen Sie Update-Database aus, um alle neuen Migrationen anzuwenden, die andere Entwickler eingecheckt haben. ** Hinweis: **** Wenn der Befehl Update-Database keine Warnungen enthält, wurden keine neuen Migrationen von anderen Entwicklern durchgeführt Es ist keine weitere Zusammenführung erforderlich.
  4. Führen Sie Add-Migration –IgnoreChanges aus (z. B. Add-Migration Merge –IgnoreChanges). Dadurch wird eine Migration mit allen Metadaten (einschließlich eines Schnappschusses des aktuellen Modells) erstellt, Änderungen, die beim Vergleich des aktuellen Modells mit dem Schnappschuss der letzten Migrationen festgestellt wurden, werden jedoch ignoriert (dh Sie erhalten eine leere Up- und Down-Methode).
  5. Weiterentwickeln oder der Quellcodeverwaltung unterziehen (natürlich nach Durchführung Ihrer Unit-Tests).

Option 2: Aktualisieren Sie den Modell-Snapshot in der letzten Migration

  1. Stellen Sie sicher, dass alle ausstehenden Modelländerungen in Ihrer lokalen Codebasis in eine Migration geschrieben wurden. Dieser Schritt stellt sicher, dass Sie keine legitimen Änderungen verpassen, wenn die leere Migration generiert wird.
  2. Synchronisieren Sie mit der Quellcodeverwaltung.
  3. Führen Sie Update-Database aus, um alle neuen Migrationen anzuwenden, die andere Entwickler eingecheckt haben. ** Hinweis: **** Wenn der Befehl Update-Database keine Warnungen enthält, wurden keine neuen Migrationen von anderen Entwicklern durchgeführt Es ist keine weitere Zusammenführung erforderlich.
  4. Führen Sie Update-Database –TargetMigration aus (im folgenden Beispiel wäre dies Update-Database –TargetMigration AddRating). Auf diese Weise wird die Datenbank auf den Status der vorletzten Migration zurückgesetzt. Die letzte Migration aus der Datenbank wird praktisch rückgängig gemacht. ** Hinweis: **** Dieser Schritt ist erforderlich, damit die Metadaten der Migration sicher bearbeitet werden können, da die Metadaten auch in der Tabelle __MigrationsHistoryTable der Datenbank gespeichert sind. Aus diesem Grund sollten Sie diese Option nur verwenden, wenn sich die letzte Migration nur in Ihrer lokalen Codebasis befindet. Wenn die letzte Migration auf andere Datenbanken angewendet wurde, müssen Sie diese ebenfalls zurücksetzen und die letzte Migration erneut anwenden, um die Metadaten zu aktualisieren.
  5. Führen Sie Add-Migration aus (in dem Beispiel, das wir verfolgt haben, wäre dies etwa Add-Migration 201311062215252_AddReaders). ** Hinweis: **** Sie müssen den Zeitstempel einfügen, damit Migrationen wissen, dass Sie die vorhandene Migration bearbeiten möchten, anstatt eine neue zu erstellen. Dadurch werden die Metadaten für die letzte Migration so aktualisiert, dass sie mit dem aktuellen Modell übereinstimmen. Sie erhalten die folgende Warnung, wenn der Befehl ausgeführt wird, aber genau das möchten Sie. „Nur der Designer-Code für die Migration '201311062215252_AddReaders' wurde umgerüstet. Verwenden Sie den -Force-Parameter, um die gesamte Migration neu zu strukturieren. “
  6. Führen Sie Update-Database aus, um die neueste Migration mit den aktualisierten Metadaten erneut anzuwenden.
  7. Weiterentwickeln oder der Quellcodeverwaltung unterziehen (natürlich nach Durchführung Ihrer Unit-Tests).

MSDN hat einen großartigen Artikel dazu. Bitte gehen Sie es durch.

Entity Framework Code Erste Migrationen in Team-Umgebungen

4
SharmaPattar

Wir haben ähnliche Probleme in unserer Umgebung, hier ist, was wir bisher herausgefunden haben und wie wir damit umgegangen sind:

Wenn Sie Änderungen vorgenommen haben, die Sie übernommen haben (Datenbank aktualisieren), aber nicht eingecheckt haben und dann Änderungen von einem anderen Entwickler erhalten, der Ihre Änderungen nicht hat, scheinen die Dinge hier nicht mehr synchron zu sein. Nach unserer Erfahrung scheinen die Metadaten, die für Ihre eigenen Änderungen gespeichert werden, durch die Metadaten des anderen Entwicklers überschrieben zu werden, wenn Sie den Datenbankaktualisierungsprozess durchführen. Der andere Entwickler hat Ihre Änderungen nicht, sodass die Metadaten, die gespeichert werden, keine wirkliche Widerspiegelung Ihrer Datenbank mehr sind. Wenn EF danach einen Vergleich durchführt, wird angenommen, dass Ihre Änderungen aufgrund der Änderung der Metadaten tatsächlich wieder neu sind.

Eine einfache, zugegebenermaßen hässliche Problemumgehung besteht darin, eine weitere Migration durchzuführen und deren Inhalt zu löschen, sodass Sie über leere up () - und leere down () -Methoden verfügen. Wenden Sie diese Migration an, checken Sie sie in die Quellcodeverwaltung ein und lassen Sie alle damit synchronisieren. Dadurch werden einfach alle Metadaten synchronisiert, sodass alle Änderungen berücksichtigt werden.

2
Brad Gardner

Ich habe ein Codeplex-Problem hinzugefügt, das auch in unserem Team viele Kopfschmerzen verursacht.

Der Link lautet https://entityframework.codeplex.com/workitem/1670

1
BrownCow

Ich habe darüber nachgedacht und hoffe, dass ich zu den verschiedenen Meinungen und Praktiken beitragen kann, die hier vorgestellt werden.

Überlegen Sie, was Ihre lokalen Migrationen tatsächlich darstellen. Wenn ich lokal mit einer Entwicklerdatenbank arbeite, verwende ich Migrationen, um die Datenbank so bequem wie möglich zu aktualisieren, wenn ich Spalten usw. zu Tabellen hinzufüge, neue Entitäten usw. hinzufüge.

Add-Migration vergleicht also mein aktuelles Modell (nennen wir es Modell b) mit meinem vorherigen Modell (Modell a) und generiert eine Migration von a => b in der Datenbank.

Für mich ist es sehr wenig sinnvoll, meine Migrationen mit anderen Migrationen zusammenzuführen, wenn tatsächlich jeder über eine eigene Datenbank verfügt und dann eine Art Stage/Test/Dev/Production-Datenbankserver in der Organisation vorhanden ist. Dies hängt alles davon ab, wie das Team es eingerichtet hat. Es ist jedoch sinnvoll, sich gegenseitig von Änderungen zu isolieren, die andere Personen vornehmen, wenn Sie wirklich auf verteilte Weise arbeiten möchten.

Nun, wenn Sie verteilt arbeiten und eine Entität haben, zum Beispiel Person, an der Sie arbeiten. Aus irgendeinem Grund arbeiten auch viele andere Leute daran. Sie können also Eigenschaften für Person hinzufügen und entfernen, die für Ihre bestimmte Story im Sprint erforderlich sind (wir arbeiten hier alle agil, oder?), Beispielsweise die Sozialversicherungsnummer, die Sie zuerst in eine Ganzzahl umgewandelt haben, weil Sie es nicht sind so hell und dann zu einer Zeichenfolge usw.

Sie fügen Vorname und Nachname hinzu.

Sie sind dann fertig und haben zehn seltsame Up- und Down-Migrationen (Sie haben wahrscheinlich einige davon während der Arbeit entfernt, da sie nur Mist waren) und Sie holen einige Änderungen aus dem zentralen Git-Repo. Beeindruckend. Ihr Kollege Bob brauchte auch ein paar Namen, vielleicht hätten Sie miteinander reden sollen?

Wie auch immer, er hat NameFirst und NameLast hinzugefügt, denke ich ... also was machst du? Nun, Sie fusionieren, refaktorieren und ändern, so dass es vernünftigere Namen hat ... wie Vorname und Nachname, Sie führen Ihre Tests durch und überprüfen seinen Code und drücken dann auf die Zentrale.

Aber was ist mit den Migrationen? Nun wäre es an der Zeit, eine Migration durchzuführen, bei der das zentrale Repo oder der Zweig "Test" im Besonderen eine nette kleine Migration von seinem Modell a enthält => Modell b. Diese Migration wird eine und nur eine Migration sein, nicht zehn seltsame.

Sehen Sie, was ich vorhabe? Wir arbeiten mit Nizza kleinen pocos und die Vergleiche von ihnen bilden die tatsächlichen Migrationen. Wir sollten also überhaupt keine Migrationen zusammenführen. Meiner Meinung nach sollten wir Migrationen pro Zweig oder ähnliches haben.

Müssen wir nach dem Zusammenführen überhaupt die Migration in der Zweigstelle erstellen? Ja, wenn diese Datenbank automatisch aktualisiert wird, müssen wir dies tun.

Eine andere Sache, die Sie berücksichtigen sollten, ist, niemals eine Migration zu erstellen, bevor Sie einen Pull aus dem zentralen Repo ausführen. Das bedeutet, dass Sie beide den Migrationscode und der anderen Teammitglieder am Modell abrufen, bevor Sie Ihre Migration erstellen.

Muss noch arbeiten, das sind zumindest meine Gedanken.

1
LavaEater

Die Lösung, die ich finden konnte (zumindest für 2 Benutzer, die noch nicht für 3 getestet wurden), ist:

  1. durch das Zusammenführen von Migrationen zum Synchronisieren der Metadaten wird die Update-Datenbank ausgeführt (dies sollte fehlschlagen)
  2. datenbank hinzufügen und dann
  3. löschen Sie den gesamten generierten Code in den Methoden up() und down()

dies wird weiterhin von der Update-Datenbank ausgeführt, führt jedoch nichts aus, sondern bringt nur die Metadaten zum Synchronisieren.

0
user2965947

Ich bin mit @LavaEater einverstanden. Der Kern des Problems scheint zu sein, dass das Migrationsgerüst zentralisiert werden sollte. Vielleicht als Teil eines automatisierten/integrierten Erstellungsprozesses bei jedem Push? Danach können die resultierenden Migrationen von Teammitgliedern vom Server abgerufen werden.

Dies bedeutet, dass ihre eigenen Migrationsskripte nicht auf den Server übertragen werden sollten.

0
user3083619