16

TFS Release Management を使用して、継続的な統合と展開を行っています。

私は、migrate.exe を使用して展開中にデータベースの移行を実行しています。これは、古いバージョンから新しいバージョンに移行するときにうまく機能します。ただし、アプリケーションの古いバージョンをデプロイする場合は、さらに複雑になります。

基本的に、コンテキストの移行を保持するアセンブリは、たとえばバージョン 3 からバージョン 2 に移行する方法を知っている必要があります。通常、展開しようとしているアセンブリを移行のソースとして使用しますが、この場合は、 v3 から v2 に移行する方法を知っているのは、既に展開されているアセンブリだけであるため、既に展開されているアセンブリを使用します。(バージョン 2 は、v3 が存在することすら知りません。)

私の現在の計画は、展開中に 2 つのアセンブリを何らかの方法で比較することです。インストール ディレクトリのアセンブリに配置ディレクタの移行よりも「新しい」移行が含まれている場合、最初に配置ディレクトリのアセンブリで利用可能な「最新の」移行を取得してから、次のコマンドを実行する必要があります。

migrate.exe AssemblyInInstallationDir /targetMigration NewestFromAssemblyInDeploymentDir

新しいバージョンにアップグレードする「通常の」展開シナリオでは、次のことができます。

migrate.exe AssemblyInDeploymentDir

これは合法的なアプローチですか?各アセンブリで利用可能な移行を評価するために EF ライブラリを使用することについては、まだ調べていません。また、これらのアセンブリのそれぞれが「同じ」でバージョンが異なるだけであるという課題もあります。おそらく、それらを別々のアプリ ドメインにロードしてから、アプリ ドメイン間の通信を使用して必要な情報を取得する必要があります。

編集

同じアセンブリの 2 つの異なるバージョンへの利用可能な移行を一覧表示できる概念実証アプリを作成しました。これはこのプロセス全体にとって非常に重要なので、文書化する価値があると考えました。

アプリはリフレクションを使用して各アセンブリを読み込み、System.Data.Entity.Migrations の DbMigrator クラスを使用して移行メタ データを列挙します。移行の名前にはタイムスタンプ情報がプレフィックスとして付けられているため、それらを注文して、どのアセンブリに「新しい」一連の移行が含まれているかを確認できます。

static void Main(string[] args)
{
    const string dllName = "Test.Data.dll";
    var assemblyCurrent = Assembly.LoadFile(Path.Combine(System.Environment.CurrentDirectory, string.Format("Current\\{0}", dllName)));
    var assemblyTarget = Assembly.LoadFile(Path.Combine(System.Environment.CurrentDirectory, string.Format("Target\\{0}", dllName)));

    Console.WriteLine("Curent Version: " + assemblyCurrent.FullName);
    Console.WriteLine("Target Version: " + assemblyTarget.FullName);

    const string contextName = "Test.Data.TestContext";
    const string migrationsNamespace = "Test.Data.Migrations";
    var currentContext = assemblyCurrent.CreateInstance(contextName);
    var targetContext = assemblyTarget.CreateInstance(contextName);

    var currentContextConfig = new DbMigrationsConfiguration
    {
        MigrationsAssembly = assemblyCurrent,
        ContextType = currentContext.GetType(),
        MigrationsNamespace = migrationsNamespace
    };

    var targetContextConfig = new DbMigrationsConfiguration
    {
        MigrationsAssembly = assemblyTarget,
        ContextType = targetContext.GetType(),
        MigrationsNamespace = migrationsNamespace
    };

    var migrator = new DbMigrator(currentContextConfig);
    var localMigrations = migrator.GetLocalMigrations(); //all migrations

    Console.WriteLine("Current Context Migrations:");
    foreach (var m in localMigrations)
    {
        Console.WriteLine("\t{0}", m);
    }

    migrator = new DbMigrator(targetContextConfig);
    localMigrations = migrator.GetLocalMigrations(); //all migrations

    Console.WriteLine("Target Context Migrations:");
    foreach (var m in localMigrations)
    {
        Console.WriteLine("\t{0}", m);
    }

    Console.ReadKey();
}

}

アプリケーションの出力は次のようになります。

Curent Version: Test.Data, Version=1.3.0.0, Culture=neutral, PublicKeyToken=null
Target Version: Test.Data, Version=1.2.0.0, Culture=neutral, PublicKeyToken=null

Current Context Migrations:
    201403171700348_InitalCreate
    201403171701519_AddedAddresInfoToCustomer
    201403171718277_RemovedStateEntity
    201403171754275_MoveAddressInformationIntoContactInfo
    201403181559219_NotSureWhatIChanged
    201403181731525_AddedRowVersionToDomainObjectBase
Target Context Migrations:
    201403171700348_InitalCreate
    201403171701519_AddedAddresInfoToCustomer
    201403171718277_RemovedStateEntity
4

2 に答える 2

3

私たちは実際にこの問題を解決し、ツールを 1 年以上使用して、完全に継続的なデータベースの運用環境へのデプロイを行っています。人間は関与していません。:)

この一部を GitHub で公開しました: https://github.com/GalenHealthcare/Galen.Ef.Deployer

「互換性を破る」変更を行うことはできますが、通常はそれも避けていますが、ほとんどの場合、アップグレード中もアプリケーションが稼働し続けるためです。私たちはデータ層を独立して展開可能なコンポーネントとして扱います。その結果、互換性を維持する必要がある「インターフェース」があります。

多くの場合、後方/前方互換性のある中間バージョンを展開し、さまざまなアプリケーション サービスをアップグレードし、最後にデータベース層をアップグレードして従来の互換性を削除する、多段階のアップグレード アプローチを使用します。

そのシナリオでも、スキーマとデータの任意のバージョンとの間を自動的に移動できます。実際、すべてのデータベース バージョンをビルドするたびに、これを検証する単体テストを追加しました。基本的に、スキーマ反復のチェーンを上/下にたどり、上向きおよび下向きの移行が常に機能し、データの一貫性と互換性を維持することを検証します。これらのテストは、GitHub プロジェクトで確認できます。次に例を示します。

https://github.com/GalenHealthcare/Galen.Ef.Deployer/blob/master/Galen.Ci.EntityFramework.Deployer/Galen.Ci.EntityFramework.Testing/MigrationTestRunner.cs

于 2015-12-17T22:29:10.150 に答える
0

私がこれにアプローチする通常の方法は、データベース スキーマに重大な変更を (ほとんど) 加えないことです。これは基本的に、制御された形式の技術的負債です。

たとえば、ColumnX を ColumnY に置き換えるとします。典型的なアプローチは、「すべてのデータを ColumnX から ColumnY にコピーし、ColumnX をスキーマから削除する」です。ColumnX がなくなるため、以前のバージョンにロールバックできなくなります。

これに取り組むロールバックしやすい方法は、ColumnY を追加し、データをコピーし、トリガーを追加して、両方の列を互いに同期させることです。これは永続的な状態を意図したものではありません! 「ColumnX と関連するトリガーを削除する」のユーザー ストーリーは、ColumnX に依存するバージョンにロールバックすることがないと確信している場合、将来の反復のためにすぐにバックログに追加されます。

ロールバックには、以前のバージョンの DACPAC の公開が引き続き含まれる可能性がありますが、スキーマではなくデータベースに存在するアイテムを削除しないようにする必要があります。このようにして、一連のストアド プロシージャを更新して ColumnY からプルする場合、ColumnX からプルする古いバージョンを公開でき、古いバージョンはスキーマが変更されたことをまったく認識しません。

于 2015-12-15T22:21:52.590 に答える