EFコアで1対1の関係を追加するときのデータの移行

.net-core c# ef-code-first entity-framework-core

質問

私のテーブルの1つに新しい1対1の関係を追加した後、私のデータベースの既存の行のデフォルトデータを追加する方法を見つけることができません。

アップグレード前にデータベースは基本的に次のようになりました。

-- Team --
  Name: TEXT

-- History --
  Id: INT

...ヒストリーは他の無関係なテーブルからそれに向けられた外部キーを持っています。

私のアップグレードでは、基本的にチームに単一の履歴を持たせて、新しいdbが次のように見えるようにします。

-- Team --
  Name: TEXT
  HistoryId: INT

-- History --
  Id: INT

しかし私の問題は、自分のDBに既存のチームがあり、ユニークな履歴行をポイントする必要があるため、既存のチームごとに新しい履歴行を作成する必要があるということです。

私はマイグレーションでUpメソッドのエントリーを手動で追加しようとしましたが、私のモデルが既存のスキーマと一致しないので、これは失敗します。

protected override void Up(MigrationBuilder migrationBuilder)
{
    migrationBuilder.AddColumn<int>(
        name: "HistoryId",
        table: "Team",
        nullable: false,
        defaultValue: 0);

    using (var db = new XMDBContext())
    {
        foreach (var team in db.Team)
            team.History = new XMHistory();
        db.SaveChanges();
    }

    migrationBuilder.CreateIndex(
        name: "IX_Team_HistoryId",
        table: "Team",
        column: "HistoryId",
        unique: true);

    migrationBuilder.AddForeignKey(
        name: "FK_Team_History_HistoryId",
        table: "Team",
        column: "HistoryId",
        principalTable: "History",
        principalColumn: "Id",
        onDelete: ReferentialAction.Cascade);
}

受け入れられた回答

残念ながら、現在、EFコアは移行によるデータのシードをサポートしていません。問題は#629 - シードデータとしてリポジトリ内で追跡されます。

私が現在見ている唯一の解決策は、 MigrationBuilder.Sqlメソッドを使用して古い良いSQLを使用することです。残念ながら、dbプロバイダサービスへのアクセスはないので、次はSQL Serverに適用されます(ただし、標準のSQLコマンドのみを使用しようとしましたが)。

元のモデルは次のようにしましょう:

public class Team
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class History
{
    public int Id { get; set; }
}

Team to HistoryからFKを追加すると、次のようになります。

public class Team
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int HistoryId { get; set; }
    public History History { get; set; }
}

自動生成された移行は次のとおりです。

protected override void Up(MigrationBuilder migrationBuilder)
{
    migrationBuilder.AddColumn<int>(
        name: "HistoryId",
        table: "Team",
        nullable: false,
        defaultValue: 0);

    migrationBuilder.CreateIndex(
        name: "IX_Team_HistoryId",
        table: "Team",
        column: "HistoryId");

    migrationBuilder.AddForeignKey(
        name: "FK_Team_History_HistoryId",
        table: "Team",
        column: "HistoryId",
        principalTable: "History",
        principalColumn: "Id",
        onDelete: ReferentialAction.Cascade);
}

CreateIndexコマンドを実行する前に、次のコマンドを手動で挿入します。

migrationBuilder.AddColumn<int>(
    name: "TeamId",
    table: "History",
    nullable: true);

migrationBuilder.Sql(@"insert into History (TeamId) select Id from Team");

migrationBuilder.Sql(@"update Team set HistoryId = (select Id from History where TeamId = Team.Id)");

migrationBuilder.DropColumn(
    name: "TeamId",
    table: "History");

アイデアは簡単です。私たちは、一時的なNULL可能列作成TeamIdにおけるHistory対応する新しいレコードを挿入し、テーブルをTeamId内の各レコードのためにTeamテーブル、そして更新HistoryIdTeam使用してテーブルTeamIdから列Historyキーとしてテーブルを、そして最終的に削除します一時的な列。

この時点で、データ変換は完了し、FK制約を作成することができます。

良い習慣からは遠いですが、回避策として使用できます。

編集: Gert Arnoldのコメントの後、SQLブロックを使うのが正しい方法です。私に関係する唯一のことは、データベースにとらわれないSQLや特定のSQLを書く方法です。もちろん、単一の特定のデータベースタイプをターゲットにしている場合、問題は存在しません。とにかく、異なるデータベースタイプを扱う必要がある場合は、 MigrationBuilder.ActiveProviderプロパティに基づいて、特定のifブロックと組み合わせて、すべてのターゲットデータベースでサポートされている標準SQLコマンドを常に使用できます。



Related

ライセンスを受けた: CC-BY-SA with attribution
所属していない Stack Overflow
このKBは合法ですか? はい、理由を学ぶ
ライセンスを受けた: CC-BY-SA with attribution
所属していない Stack Overflow
このKBは合法ですか? はい、理由を学ぶ