web-dev-qa-db-ja.com

コラボレーション環境でのEntityFrameworkでの移行

Entity Framework5.0を使用するプロジェクトに取り組んでいる複数の開発者がいます。すべての開発者は独自のローカルSQL2012データベースを使用しているため、他の開発者の邪魔をすることなく開発とテストを行うことができます。

最初は、自動移行とコードベースの移行のハイブリッドを使用しました。それはまったくうまくいかなかったので、自動移行を無効にし、コードベースのみを許可することにしました。すべての自動移行からの「破損した」_MigrationsHistoryのないクリーンなデータベースから再開したことを付け加えておきます。

したがって、ワークフローは次のようになります。

  1. 開発者がデータモデルを変更する
  2. add-migration <Name>を実行し、update-databaseを使用してデータベースに適用します。
  3. データモデルの変更とGitへの移行をチェックインします。
  4. 別の開発者がプルし、変更を受け取り、それを自分のデータベースに適用します。

これまでのところ、これはうまくいきました。しかし、今日以前は、移行を行ったのは通常私だけであり、他の人がそれらを適用していました。しかし、今日、3人の開発者からの移行がありました。私はそれらの移行をプルし、update-databaseを実行しました。

自分のデータモデルにも変更があったので、update-databaseの最後に、まだ最新ではないという警告が表示されたので、add-migration <my migration>を実行しました。ただし、移行の足場を組むと、データベースにすでに適用したすべての移行の変更が表示されました。つまり、すでに削除された列を削除しようとしたり、既存のテーブルを作成しようとしたりしました。

どうしてそれができるのでしょうか?私の想定では、EFは_MigrationsHistoryテーブルをチェックして、テーブルにまだ存在していない移行を見つけ、名前の一部であるタイムスタンプ順に並べて適用します。しかし、明らかにそうではありません。自分の変更を元に戻し、クリーンな環境を使用している場合でも、データベースがモデルと同期していないと文句を言うからです。しかし、私はそれらの変更をプルしてデータベースに適用しただけです。 同期しています。 _MigrationsHistoryテーブルにも適用したばかりの移行を確認できます。

私が考えることができる唯一のことは、データベースの変更をもたらさないプロパティをデータモデルに追加したことです(データモデルYにList<X>を追加しました。ここで、Xは1対多の関係の多です。 。XにはすでにYへの外部キーがあるため、これによってデータベースが変更されることはありません。それでいいの?もしそうなら、データベースの変更がなく、これを修正する方法もわからないため、そのための移行を追加する方法がないため、それは本当に壊れやすいです。

もちろん、足場を編集して、データベースにすでに適用されているものをすべて削除できるため、これに対処する方法がわかりません。しかし、それでは何ですか?私がチェックインすると、他の開発者は、新しい変更を適用した後でもデータベースが最新ではないという同じメッセージを受け取り、自分の変更をスキャフォールディングし、同じナンセンスなスキャフォールディングを取得し、編集し、チェックインしてからnext開発者が取得します。これは悪循環になり、自動移行を使用したときと同じようになり、コードベースのみに切り替えることで修正されたと思いました。私は今、正しいことをすることを信じることができず、このように働くことは悪夢です。

また、同僚から取得した移行をupdate-database -t:201211091112102_<migrationname>で1つずつ追加しようとしましたが、役に立ちませんでした。それでも私に誤った足場を与えます。

では、ここで何を間違えたのでしょうか。それとも、EFはこのようなコラボレーションのために構築されたものではないのでしょうか。

[〜#〜]更新[〜#〜]

再現可能なテストケースを作成しましたが、このマルチユーザー/マルチデータベースのシナリオをシミュレートするために、少し長いダンスです。

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

上記のプロジェクトがある場合に再現する手順(これらの手順はコードにも含まれています):

  1. add-migration Init
  2. update-database(データベース 'TestDb'上)
  3. TestDb1を指すように接続文字列を変更します
  4. testDb1の更新データベース
  5. クラステストでプロパティFooのコメントを解除します
  6. add-migration M1を使用して、プロパティFooをTestDb1に追加します
  7. Test.Fooをもう一度コメントアウトする
  8. TestDb2を指すように接続文字列を変更します
  9. TestDb2に適用されないように、移行M1をプロジェクトから除外します
  10. クラステストでプロパティバーのコメントを解除します
  11. update-データベースを更新して、初期移行をTestDb2に適用します
  12. add-migration M2を使用して、プロパティバーをTestDb2に追加します
  13. 元のTestDbを再び指すように接続文字列を変更します
  14. 移行M1をプロジェクトに再度含めます
  15. クラステストでプロパティFooのコメントを解除します
  16. クラスTestでプロパティSomeIntのコメントを解除します
  17. データベースを更新する
  18. 追加移行M3
  19. update-database、M3が移行M1によってすでに追加されたデータベースTestDbに列Fooを追加しようとするため、エラーが発生します。

上記は3人のユーザーをシミュレートするためのもので、ユーザー1がデータベースを初期化し、他の2人は初期化を使用してデータベースを作成します。次に、ユーザー2とユーザー3の両方がデータモデルに独自の変更を加え、変更を適用するために必要な移行とともにソース管理に追加します。次に、ユーザー1がユーザー2と3の変更をプルし、ユーザー1もデータベースに変更を加えました。次に、ユーザー1はupdate-databaseを呼び出して、ユーザー2と3の変更を適用します。次に、ユーザー2または3からの変更を誤ってスキャフォールド移行に追加し、ユーザー1のデータベースに適用するとエラーが発生する独自の移行をスキャフォールディングします。 。

45
JulianR

コードの競合と同じように、移行の競合を手動で解決する必要があります。更新して新しい移行がある場合は、最後の移行の背後にあるメタデータが現在のモデルと一致していることを確認する必要があります。移行のメタデータを更新するには、移行のAdd-Migrationコマンドを再発行します。

たとえば、シナリオのステップ17(Update-Database)の前に、次のコマンドを発行する必要があります

Add-Migration M2

これにより、メタデータが更新され、現在のモデルと同期されます。これで、M3を追加しようとすると、それ以上モデルを変更していないため、空白になっているはずです。

5
bricelam

.resxファイル内の最新の移行のスナップショットをリセットする空白の「マージ」移行を追加する必要があります。 IgnoreChangesスイッチを使用してこれを行います。

Add-Migration <migration name> -IgnoreChanges

説明については ここ を参照してください

18
Dewey

オプション1:空白の「マージ」移行を追加します

  1. ローカルコードベースで保留中のモデル変更が移行に書き込まれていることを確認します。この手順により、空白の移行を生成するときに正当な変更を見逃さないようにします。
  2. ソース管理と同期します。
  3. Update-Databaseを実行して、他の開発者がチェックインした新しい移行を適用します。**注:**** Update-Databaseコマンドから警告が表示されない場合、他の開発者からの新しい移行はありません。それ以上のマージを実行する必要はありません。
  4. Add-Migration –IgnoreChangesを実行します(例:Add-Migration Merge –IgnoreChanges)。これにより、すべてのメタデータ(現在のモデルのスナップショットを含む)を含む移行が生成されますが、現在のモデルを最後の移行のスナップショットと比較したときに検出された変更は無視されます(つまり、空白のUpおよびDownメソッドが取得されます)。
  5. 開発を続行するか、ソース管理に送信します(もちろんユニットテストを実行した後)。

オプション2:最後の移行でモデルスナップショットを更新します

  1. ローカルコードベースで保留中のモデル変更が移行に書き込まれていることを確認します。この手順により、空白の移行を生成するときに正当な変更を見逃さないようにします。
  2. ソース管理と同期します。
  3. Update-Databaseを実行して、他の開発者がチェックインした新しい移行を適用します。**注:**** Update-Databaseコマンドから警告が表示されない場合、他の開発者からの新しい移行はありません。それ以上のマージを実行する必要はありません。
  4. Update-Database –TargetMigrationを実行します(これをフォローしている例では、Update-Database –TargetMigration AddRatingになります)。これにより、データベースは最後から2番目の移行の状態に戻り、データベースからの最後の移行を効果的に「適用解除」します。 **注:****メタデータはデータベースの__MigrationsHistoryTableにも保存されるため、この手順は、移行のメタデータを安全に編集できるようにするために必要です。これが、最後の移行がローカルコードベースのみにある場合にのみこのオプションを使用する必要がある理由です。他のデータベースに最後の移行が適用されている場合は、それらをロールバックし、最後の移行を再適用してメタデータを更新する必要があります。
  5. Add-Migrationを実行します(これをフォローしている例では、Add-Migration 201311062215252_AddReadersのようになります)。 **注:****新しい移行をスキャフォールディングするのではなく、既存の移行を編集することを移行が認識できるように、タイムスタンプを含める必要があります。これにより、最後の移行のメタデータが現在のモデルと一致するように更新されます。コマンドが完了すると次の警告が表示されますが、それはまさにあなたが望むものです。 「移行用のデザイナーコード「201311062215252_AddReaders」のみが再スキャフォールドされました。移行全体を再スキャッフォードするには、-Forceパラメーターを使用します。」
  6. Update-Databaseを実行して、更新されたメタデータを使用して最新の移行を再適用します。
  7. 開発を続行するか、ソース管理に送信します(もちろんユニットテストを実行した後)。

MSDNには、これに関するすばらしい記事があります。それを通過してください。

チーム環境でのEntity Frameworkコードの最初の移行

5
SharmaPattar

私たちの環境でも同様の問題が発生しています。これまでに把握したことと、それを回避する方法は次のとおりです。

適用した変更(update-database)があるがチェックインしていない場合、変更がない別の開発者から変更を受け取った場合、ここで状況が同期していないように見えます。私たちの経験では、自分の変更のために保存されたメタデータは、データベースの更新プロセスを実行するときに、他の開発者からのメタデータによって上書きされるようです。他の開発者はあなたの変更を持っていないので、保存されるメタデータはもはやあなたのデータベースの実際の反映ではありません。その後EFが比較を行うと、メタデータの変更により、変更が実際に再び新しいものであると「考えられます」。

単純で明らかに醜い回避策は、別の移行を実行し、その内容を消去して、空のup()メソッドと空のdown()メソッドを作成することです。その移行を適用してソース管理にチェックインし、全員がそれに同期できるようにします。これは単にすべてのメタデータを同期するので、誰もがすべての変更を説明できます。

2
Brad Gardner

Codeplexに問題を追加しました。この問題により、私たちのチームでも多くの頭を悩ませています。

リンクは https://entityframework.codeplex.com/workitem/167

1
BrownCow

私はこれについていくつか考えました。ここに提示されたさまざまな意見や慣行に貢献したいと思います。

ローカル移行が実際に何を表しているかを検討してください。開発データベースをローカルで操作する場合、テーブルに列などを追加したり、新しいエンティティを追加したりするときに、可能な限り最も便利な方法でデータベースを更新するために移行を使用します。

したがって、Add-Migrationは、現在のモデル(モデルbと呼びましょう)を以前のモデル(モデルa)と照合し、データベース内のa => bから移行するための移行を生成します。

誰もが実際に独自のデータベースを持っていて、組織内に何らかのステージ/テスト/開発/本番データベースサーバーが存在する場合、自分の移行を他の人の移行とマージしようとすることはほとんど意味がありません。これはすべて、チームがどのように設定したかによって異なりますが、本当に分散して作業したい場合は、他の人が行った変更からお互いを隔離することは理にかなっています。

さて、あなたが分散して仕事をしていて、あなたが取り組んでいるエンティティ、例えば、Personを持っているなら。どういうわけか、他の多くの人々もそれに取り組んでいます。したがって、スプリントの特定のストーリーの必要に応じて、Personのプロパティを追加および削除します(ここではすべてアジャイルに取り組んでいますね)。たとえば、社会保障番号を最初に整数にしたのは、そうではないからです。その明るいそしてそれからひもなどに。

FirstNameとLastNameを追加します。

その後、完了し、10個の奇妙な上下の移行があり(それらはただのがらくただったので、おそらく作業中にそれらのいくつかを削除しました)、中央のGitリポジトリからいくつかの変更をフェッチします。ワオ。あなたの同僚のボブもいくつかの名前を必要としていました、多分あなたはお互いに話すべきでしたか?

とにかく、彼はNameFirstとNameLastを追加したと思います...それであなたはどうしますか?さて、あなたはマージし、リファクタリングし、変更して、より正しい名前を付けます... FirstNameやLastNameのように、テストを実行して彼のコードをチェックしてから、中央にプッシュします。

しかし、移行についてはどうでしょうか?さて、今こそ、中央リポジトリを移動する移行を行うときです。つまり、ブランチの「テスト」には、モデルa =>モデルbからのちょっとした移行が含まれています。この移行は、10の奇妙な移行ではなく、たった1つの移行になります。

私が何をしているのか分かりますか?私たちは素敵な小さなポコと協力しており、それらの比較が実際の移行を構成しています。したがって、移行をマージするべきではありません。私の意見では、ブランチごとの移行などを行う必要があります。

実際、マージ後にブランチで移行を作成する必要さえありますか?はい、このデータベースが自動的に更新される場合は、更新する必要があります。

考慮すべきもう1つのことは、中央リポジトリからプルを実行する前に、実際に移行を作成しないことです。つまり、移行を作成する前に、他のチームメンバーの移行コードおよびモデルへの変更を両方とも取得します。

もう少し作業する必要があります。少なくとも、これについての私の考えです。

1
LavaEater

私が思いついた解決策(少なくとも2人のユーザー、3人のテストは行っていません)は次のとおりです。

  1. 移行をマージしてメタデータを同期し、update-databaseを実行します(これは失敗するはずです)。
  2. データベースを追加してから
  3. up()およびdown()メソッドで生成されたすべてのコードを削除します

これは引き続き更新データベースによって実行されますが、メタデータを同期するだけで何も実行されません。

0
user2965947

@LavaEaterに同意します。問題の核心は、移行の足場を一元化する必要があることだと思われます。おそらく、プッシュが発生するたびに、自動化/統合されたビルドプロセスの一部としてですか?その後、結果として生じる移行は、チームメンバーによってサーバーからプルできます。

これは、独自の移行スクリプトをサーバーにプッシュしないことを意味します。

0
user3083619