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:
add-migration <Name>
und wendet es mit update-database
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):
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.
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.
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
Option 1: Fügen Sie eine leere "Zusammenführungs" -Migration hinzu
- 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.
- Mit der Quellcodeverwaltung synchronisieren.
- 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.
- 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).
- Weiterentwickeln oder der Quellcodeverwaltung unterziehen (natürlich nach Durchführung Ihrer Unit-Tests).
Option 2: Aktualisieren Sie den Modell-Snapshot in der letzten Migration
- 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.
- Synchronisieren Sie mit der Quellcodeverwaltung.
- 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.
- 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.
- 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. “
- Führen Sie Update-Database aus, um die neueste Migration mit den aktualisierten Metadaten erneut anzuwenden.
- 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.
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.
Ich habe ein Codeplex-Problem hinzugefügt, das auch in unserem Team viele Kopfschmerzen verursacht.
Der Link lautet https://entityframework.codeplex.com/workitem/1670
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.
Die Lösung, die ich finden konnte (zumindest für 2 Benutzer, die noch nicht für 3 getestet wurden), ist:
up()
und down()
dies wird weiterhin von der Update-Datenbank ausgeführt, führt jedoch nichts aus, sondern bringt nur die Metadaten zum Synchronisieren.
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.