Entity Framework CoreでPK定義の変更をする方法
2023-01-04
azblob://2023/01/04/eyecatch/2023-01-05-entity-framework-core-customize-migration-code-pk-000.jpg

こんにちは、あおいです。

さて、前回の記事ではマイグレーション時に自動生成される移行コードをカスタマイズする方法について紹介させていただきました。
Entity Framework Coreで移行コードをカスタマイズする方法 | cloud config Tech Blog

PK(主キー)の定義を変更する場合も、移行コードをスクラッチでカスタマイズする必要があります。
 

例として桁数(10⇒15)を変更すると、EF Coreによって次の移行が生成されます。

C#protected override void Up(MigrationBuilder migrationBuilder)
{
    migrationBuilder.AlterColumn<string>(
        name: "Name",
        table: "People",
        type: "nvarchar(15)",
        nullable: false,
        oldClrType: typeof(string),
        oldType: "nvarchar(10)");
}

一見、これで良さそうに見えますが、ダメです。マイグレーション時に依存関係でエラーを吐きます。

オブジェクト「PK_○○」は列「○○」に依存しています。1 つ以上のオブジェクトがこの列にアクセスしているため、ALTER TABLE ALTER COLUMN は失敗しました。

そこで、今回はEntity Framework CoreでPK定義の変更をする方法について紹介したいと思います。

PK依存の場合

解決策として、以下の手順で移行コードをスクラッチでカスタマイズします。

  1. 主キー制約を削除 ← DropPrimaryKeyを追加で記述
  2. AlterColumn ← そのままでOK
  3. 主キー制約を作成 ← AddPrimaryKeyを追加で記述
C#
using Microsoft.EntityFrameworkCore.Migrations;

#nullable disable

namespace EFCoreSample.Migrations
{
    public partial class second : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            // スクラッチで記述 ↓
            migrationBuilder.DropPrimaryKey(
                name: "PK_People",
                table: "People");
            // スクラッチで記述↑

            migrationBuilder.AlterColumn<string>(
                name: "Name",
                table: "People",
                type: "nvarchar(15)",
                nullable: false,
                oldClrType: typeof(string),
                oldType: "nvarchar(10)");

            // スクラッチで記述 ↓
            migrationBuilder.AddPrimaryKey(
                name: "PK_People",
                table: "People",
                column: "Name");
            // スクラッチで記述 ↑
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {

            // スクラッチで記述 ↓
            migrationBuilder.DropPrimaryKey(
                name: "PK_People",
                table: "People");
            // スクラッチで記述↑

            migrationBuilder.AlterColumn<string>(
                name: "Name",
                table: "People",
                type: "nvarchar(10)",
                nullable: false,
                oldClrType: typeof(string),
                oldType: "nvarchar(15)");

            // スクラッチで記述 ↓
            migrationBuilder.AddPrimaryKey(
                name: "PK_People",
                table: "People",
                column: "Name");
            // スクラッチで記述 ↑
        }
    }
}

MigrationBuilderクラスのメソッド一覧 | Microsoft Docs

PK・FK依存の場合

PKだけでなく、FK依存もある場合、以下の手順で移行コードをスクラッチでカスタマイズします。

  1. 外部キー制約を削除 ← DropForeignKeyを追加で記述
  2. 主キー制約を削除 ← DropPrimaryKeyを追加で記述
  3. AlterColumn ← そのままでOK
  4. 主キー制約を作成 ← AddPrimaryKeyを追加で記述
  5. 外部キー制約を作成 ← AddForeignKeyを追加で記述
C#using Microsoft.EntityFrameworkCore.Migrations;

#nullable disable

namespace EFCoreSample.Migrations
{
    public partial class second : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            // スクラッチで記述 ここから ↓
            migrationBuilder.DropForeignKey(
                name: "company_fkey",
                table: "Employees");

            migrationBuilder.DropPrimaryKey(
                name: "PK_Companys",
                table: "Companys");
            // スクラッチで記述 ここまで ↑


            migrationBuilder.AlterColumn<string>(
                name: "CompanyId",
                table: "Employees",
                type: "char(10)",
                nullable: false,
                oldClrType: typeof(string),
                oldType: "char(8)");

            migrationBuilder.AlterColumn<string>(
                name: "CompanyId",
                table: "Companys",
                type: "char(10)",
                nullable: false,
                oldClrType: typeof(string),
                oldType: "char(8)");


            // スクラッチで記述 ここから ↓
            migrationBuilder.AddPrimaryKey(
                name: "PK_Companys",
                table: "Companys",
                column: "CompanyId");

            migrationBuilder.AddForeignKey(
                name: "company_fkey",
                table: "Employees",
                column: "CompanyId",
                principalTable: "Companys",
                principalColumn: "CompanyId",
                onDelete: ReferentialAction.Cascade);
            // スクラッチで記述 ここまで ↑
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
            // スクラッチで記述 ここから ↓
            migrationBuilder.DropForeignKey(
                name: "company_fkey",
                table: "Employees");

            migrationBuilder.DropPrimaryKey(
                name: "PK_Companys",
                table: "Companys");
            // スクラッチで記述 ここまで ↑


            migrationBuilder.AlterColumn<string>(
                name: "CompanyId",
                table: "Employees",
                type: "char(8)",
                nullable: false,
                oldClrType: typeof(string),
                oldType: "char(10)");

            migrationBuilder.AlterColumn<string>(
                name: "CompanyId",
                table: "Companys",
                type: "char(8)",
                nullable: false,
                oldClrType: typeof(string),
                oldType: "char(10)");


            // スクラッチで記述 ここから ↓
            migrationBuilder.AddPrimaryKey(
                name: "PK_Companys",
                table: "Companys",
                column: "CompanyId");

            migrationBuilder.AddForeignKey(
                name: "company_fkey",
                table: "Employees",
                column: "CompanyId",
                principalTable: "Companys",
                principalColumn: "CompanyId",
                onDelete: ReferentialAction.Cascade);
            // スクラッチで記述 ここまで ↑
        }
    }
}

MigrationBuilderクラスのメソッド一覧 | Microsoft Docs

今回はEntity Framework CoreでPK定義を変更する方法について紹介させていただきました。
コード全体を見ると、慣れない記述形式で最初は身構えてしまうかもしれませんが、手順は通常のSQLクエリと同じです。
本記事が少しでも読者の皆様のお役に立てれば幸いです。