移行の管理

モデルが変更されると、通常の開発の一環として移行が追加および削除され、移行ファイルがプロジェクトのソース管理にチェックインされます。 移行を管理するには、まず EF Core コマンド ライン ツールをインストールする必要があります。

Tip

DbContextがスタートアップ プロジェクトとは異なるアセンブリにある場合は、パッケージ マネージャー コンソール ツールまたは .NET CLI ツールでターゲット プロジェクトとスタートアップ プロジェクトを明示的に指定できます。

移行を追加する

モデルが変更されたら、その変更の移行を追加できます。

dotnet ef migrations add AddBlogCreatedTimestamp

移行名は、バージョン管理システムのコミット メッセージのように使用できます。 たとえば、変更が エンティティの新しいCreatedTimestamp プロパティである場合は、Blog のような名前を選択できます。

Migrations ディレクトリの下に、次の 3 つのファイルがプロジェクトに追加されます。

  • XXXXXXXXXXXXXX_AddBlogCreatedTimestamp.cs - メインの移行ファイル。 ( Up) 移行を適用し、( Downで) 元に戻すために必要な操作が含まれています。
  • XXXXXXXXXXXXXX_AddBlogCreatedTimestamp.Designer.cs - 移行メタデータ ファイル。 EF で使用される情報が含まれています。
  • MyContextModelSnapshot.cs - 現在のモデルのスナップショット。 次の移行を追加するときに何が変更されたかを確認するために使用されます。

ファイル名のタイムスタンプは、変更の進行状況を確認できるように、時系列に並べ替えるのに役立ちます。

Namespaces

移行ファイルを自由に移動し、その名前空間を手動で変更できます。 新しい移行は前回の移行の兄弟として作成されます。 または、次のように、生成時にディレクトリを指定することもできます。

dotnet ef migrations add InitialCreate --output-dir Your/Directory

Note

--namespaceを使用して、ディレクトリとは別に名前空間を変更することもできます。

1 つの手順で移行を作成して適用する

Note

この機能は EF Core 11 で追加されました。

dotnet ef database update コマンドは、--add オプションを使用した単一のステップでの移行の作成と適用をサポートします。 これにより、Roslyn を使用して実行時に移行がコンパイルされ、アプリケーションを停止して再構築できない.NET Aspireやコンテナー化されたアプリケーションなどのシナリオが可能になります。

dotnet ef database update InitialCreate --add

dotnet ef migrations addで使用できるのと同じオプションを使用できます。

dotnet ef database update AddProducts --add --output-dir Migrations/Products --namespace MyApp.Migrations

このコマンドは、指定した名前で新しい移行をスキャフォールディングし、Roslyn を使用してコンパイルし、すぐにデータベースに適用します。 移行ファイルは、ソース管理と今後の再コンパイルのためにディスクに保存されます。

保留中のモデル変更が検出されない場合、コマンドは新しい移行を作成せずに既存の保留中の移行を適用します。

移行コードをカスタマイズする

EF Core は通常、正確な移行を作成しますが、常にコードを確認し、目的の変更に対応していることを確認する必要があります。場合によっては、そうする必要さえある。

列の名前変更

移行のカスタマイズが必要な注目すべき例の 1 つは、プロパティの名前を変更する場合です。 たとえば、プロパティの名前を Name から FullName に変更すると、EF Core によって次の移行が生成されます。

migrationBuilder.DropColumn(
    name: "Name",
    table: "Customers");

migrationBuilder.AddColumn<string>(
    name: "FullName",
    table: "Customers",
    nullable: true);

通常、EF Core では、列を削除して新しい列を作成する (2 つの個別の変更)、および列の名前を変更する必要があるタイミングを知ることができません。 上記の移行が as-is適用されると、すべての顧客名が失われます。 列の名前を変更するには、上記で生成された移行を次のように置き換えます。

migrationBuilder.RenameColumn(
    name: "Name",
    table: "Customers",
    newName: "FullName");

Tip

移行スキャフォールディング プロセスは、操作によってデータが失われる可能性がある場合に警告します (列の削除など)。 その警告が表示される場合は、特に移行コードの精度を確認してください。

データ操作

移行では、データを移動したり、スキーマを変更したりできます。 移行の書き込み時に値が認識されるかどうかに基づいて、操作を選択します。

  • 明示的なキーによって識別される固定値と行には、 InsertDataUpdateData、および DeleteData を使用します。 EF Core は、これらの操作をプロバイダー固有の SQL に変換するため、スクリプトとバンドルを生成するときにも機能します。
  • 既存のデータベース データから新しい値を計算する必要がある場合は、 Sql を使用します。 SQL 構文はプロバイダーによって異なる場合があります。必要に応じて、 MigrationBuilder.ActiveProvider にブランチします。
  • 再利用可能な操作でプロバイダー固有の SQL 生成が必要な場合に 、カスタム移行 操作を定義します。

移行でデータを移動するために、現在の DbContext またはエンティティ CLR 型を使用しないでください。 移行履歴は、これらの型が変更または削除された後も、引き続きコンパイルおよび動作する必要があります。

既存のデータを変換する

列を置き換える場合は、変換先が設定されるまでソース データを保持します。

  1. 変換先列を null 許容として追加します。
  2. 既存の列から設定します。
  3. 必要に応じて、変換先列を必須にします。
  4. ソース列を削除します。

次の移行では、SQL Serverと SQLite のシーケンスを実装します。

migrationBuilder.AddColumn<string>(
    name: "FullName",
    table: "Customers",
    nullable: true);

if (migrationBuilder.ActiveProvider == "Microsoft.EntityFrameworkCore.SqlServer")
{
    migrationBuilder.Sql(
        """
        UPDATE [Customers]
        SET [FullName] = [FirstName] + N' ' + [LastName];
        """);
}
else if (migrationBuilder.ActiveProvider == "Microsoft.EntityFrameworkCore.Sqlite")
{
    migrationBuilder.Sql(
        """
        UPDATE "Customers"
        SET "FullName" = "FirstName" || ' ' || "LastName";
        """);
}
else
{
    throw new NotSupportedException(
        $"Data migration is not implemented for provider {migrationBuilder.ActiveProvider}.");
}

migrationBuilder.AlterColumn<string>(
    name: "FullName",
    table: "Customers",
    nullable: false,
    oldClrType: typeof(string),
    oldNullable: true);

migrationBuilder.DropColumn(
    name: "FirstName",
    table: "Customers");

migrationBuilder.DropColumn(
    name: "LastName",
    table: "Customers");

アプリケーションがサポートするすべてのプロバイダーにブランチを追加します。 不明なプロバイダーをスローする方が、不完全な移行をサイレント モードで適用するよりも安全です。 信頼されていない値から SQL を構築しないでください。移行 SQL は、スキーマ変更権限を使用して実行されます。

一部の変換は、情報を失わずに元に戻すことはできません。 元の値を安全に再構築できる場合にのみ、 Down を実装します。 それ以外の場合は、明示的に失敗し、ロールバック 手順の一部としてバックアップからデータを復元する必要があります。

固定データを挿入する

移行の書き込み時にキーと値がわかっている場合は、 InsertData を使用します。

migrationBuilder.InsertData(
    table: "Countries",
    columns: new[] { "CountryId", "Name" },
    values: new object[,]
    {
        { 1, "United States" },
        { 2, "Canada" }
    });

対応する Down メソッドは、同じキーを使用して DeleteData を呼び出す必要があります。

固定データの更新

UpdateData はキーによって行を識別し、1 つ以上の列を固定値に設定します。

migrationBuilder.UpdateData(
    table: "Countries",
    keyColumn: "CountryId",
    keyValue: 1,
    column: "Name",
    value: "United States of America");

Downメソッドは、前の値を復元する必要があります。

固定データを削除する

DeleteData キーによって行も識別されます。

migrationBuilder.DeleteData(
    table: "Countries",
    keyColumn: "CountryId",
    keyValue: 2);

削除を元に戻す必要がある場合、 Down メソッドは削除されたすべての値を復元するために InsertData を使用する必要があります。 これらの操作では、現在のデータベースの状態は照会されません。動作が既存のデータに依存する場合は、 Sql または初期化時のシード処理を使用します。

生 SQL を使用した任意の変更

生 SQL を使用して、EF Core が認識していないデータベース オブジェクトを管理することもできます。 これを行うには、モデルを変更せずに移行を追加します。空の移行が生成され、生の SQL 操作を設定できます。

たとえば、次の移行では SQL Server ストアド プロシージャが作成されます。

migrationBuilder.Sql(
@"
    EXEC ('CREATE PROCEDURE getFullName
        @LastName nvarchar(50),
        @FirstName nvarchar(50)
    AS
        SELECT @LastName + @FirstName;')");

Tip

EXEC は、ステートメントが SQL バッチの最初のステートメントまたは唯一のステートメントである必要がある場合に使用されます。 また、参照される列が現在テーブルに存在しない場合に発生する可能性のある冪等移行スクリプトのパーサーエラーを回避するためにも使用できます。

これを使用して、次のようなデータベースのあらゆる側面を管理できます。

  • ストアド プロシージャ
  • フルテキスト検索
  • Functions
  • Triggers
  • Views

ほとんどの場合、EF Core は、移行を適用するときに、各移行を独自のトランザクションで自動的にラップします。 残念ながら、一部のデータベースのトランザクション内で一部の移行操作を実行することはできません。このような場合は、suppressTransaction: truemigrationBuilder.Sqlを渡すことによって、トランザクションをオプトアウトできます。

Note

EF Core 9 では、EF Core は既定で 1 つのトランザクションで保留中のすべての移行にまたがっています (これは EF Core 10 で元に戻されました)。 詳細については 、破壊的変更に関するメモを 参照してください。

移行を削除する

移行を追加し、EF Core モデルを適用する前に追加の変更を加える必要がある場合があります。 最後の移行を削除するには、このコマンドを使用します。

dotnet ef migrations remove

移行を削除した後、追加のモデル変更を行い、もう一度追加することができます。

Warning

運用データベースに既に適用されている移行は削除しないでください。 そうすることで、これらの移行をデータベースから元に戻すことができなくなります。また、後続の移行によって行われた想定が損なわれる可能性があります。

移行がローカルに適用された場合

破棄可能な開発データベースの場合は、最初にデータベースを以前の移行に更新してから、プロジェクトから移行を削除します。 最初の移行を削除するときは、ターゲットとして 0 を使用します。

dotnet ef database update PreviousMigration
dotnet ef migrations remove

または、 --force は両方の手順を実行します。

dotnet ef migrations remove --force

移行が共有データベースに適用された場合

共有、テスト、または運用データベースに適用されている移行は削除しないでください。 通常、プロジェクトで移行を維持し、新しい修正移行を追加します。 計画的なロールバックが必要な場合は、元の移行コードを引き続き使用できる状態でロールバックを実行し、アプリケーションとデータベースのデプロイを調整します。

適用されていない古い移行を削除する

ツールは、最新の移行のみを削除します。 シーケンスの途中から移行を削除し、モデル スナップショットを手動で編集しないでください。 移行とその後のすべての移行が発行され、適用されていない場合は、後の移行を逆の順序で削除し、不要な移行を削除してから、保持されているモデルの変更をもう一度スキャフォールディングします。

移行が異なるブランチで作成された場合は、代わりに 分岐した移行ツリー のワークフローに従ってください。

移行の一覧表示

既存のすべての移行を次のように一覧表示できます。

dotnet ef migrations list

移行の状態をプログラムで調べることもできます。

var allMigrations = context.Database.GetMigrations();
var appliedMigrations = await context.Database.GetAppliedMigrationsAsync();
var pendingMigrations = await context.Database.GetPendingMigrationsAsync();

GetPendingMigrationsAsync は、構成済みの移行アセンブリ内の移行と、ターゲット データベースに記録された移行を比較します。 移行でキャプチャされていないモデルの変更は検出されません。その場合は、以下の保留中のモデル変更チェックを使用してください。

保留中のモデル変更の確認

Note

この機能は EF Core 8.0 で追加されました。

最後の移行以降にモデルの変更が行われたかどうかを確認したい場合があります。 これは、自分またはチームメイトが移行を追加するのを忘れたときに知るのに役立ちます。 これを行う 1 つの方法は、このコマンドを使用することです。

dotnet ef migrations has-pending-model-changes

context.Database.HasPendingModelChanges()を使用してプログラムでこのチェックを実行することもできます。 これは、移行の追加を忘れた場合に失敗する単体テストを記述するために使用できます。

Note

EF Core 9 以降、モデルの変更が保留中の状態で Migrate または MigrateAsync を呼び出すと、例外 (イベント ID PendingModelChangesWarning) が発生します。 詳細については、 移行の適用に関するドキュメント破壊的変更に関するメモを 参照してください。

すべての移行のリセット

極端な場合には、すべての移行を削除して最初からやり直す必要があります。 これを簡単に行うには、 Migrations フォルダーを削除し、データベースを削除します。その時点で、現在のスキーマ全体を含む新しい初期移行を作成できます。

また、すべての移行をリセットし、データを失うことなく単一の移行を作成することもできます。 これは スカッシュ 移行と呼ばれ、手動での作業が含まれます。 EF Core では現在、自動スカッシュ コマンドは提供されていません。 dotnet/efcore#2174 を参照してください。

  1. 問題が発生した場合に備え、データベースをバックアップします。
  2. データベースで、移行履歴テーブルからすべての行を削除します (SQL Server の DELETE FROM [__EFMigrationsHistory] など)。
  3. Migrations フォルダーを削除します。
  4. 新しい移行を作成し、その SQL スクリプトを生成します (dotnet ef migrations script)。
  5. テーブルが既に存在するため、移行履歴に 1 行を挿入して、最初の移行が既に適用されていることを記録します。 INSERT SQL は、上記で生成された SQL スクリプトの最後の操作であり、次のようになります (値の更新を忘れないでください)。
INSERT INTO [__EFMigrationsHistory] ([MIGRATIONID], [PRODUCTVERSION])
VALUES (N'<full_migration_timestamp_and_name>', N'<EF_version>');

Warning

Migrations フォルダーが削除されると、カスタム移行コードは失われます。 新しい初期移行を保持するには、カスタマイズを手動で適用する必要があります。

スカッシュする前に、デプロイされたすべてのデータベースが既知の移行にあることを確認し、バックアップします。 新しい初期移行から新しいデータベースを作成する必要があります。既存のデータベースでは、既に適用されているスキーマ操作を実行せずに置換移行が記録されている必要があります。 デプロイの前に両方のパスをテストします。

その他のリソース