web-dev-qa-db-ja.com

EF5 Code First-移行に伴う列タイプの変更

私はEF5 Code Firstが初めてであり、職場でプロジェクトに着手する前に概念実証をいじくり回しています。

最初に次のようなモデルを作成しました

public class Person {
  public int Id { get; set; }
  public string FirstName { get; set;}
  public string Surname {get;set;}
  public string Location {get;set;}
}

そして、一番上に引っかかった小さなMVCアプリケーションを使用して、いくつかのレコードを追加しました。

ここで、Location列を次のような列挙型に変更します。

public class Person {
  public int Id { get; set; }
  public string FirstName { get; set;}
  public string Surname {get;set;}
  public Locations Location {get;set;}
}

public enum Locations {
  London = 1,
  Edinburgh = 2,
  Cardiff = 3
}

新しい移行を追加すると、次のようになります。

AlterColumn("dbo.People", "Location", c => c.Int(nullable: false));

しかし、データベースの更新を実行するとエラーが発生します

Conversion failed when converting the nvarchar value 'London' to data type int.

移行時に、alterステートメントを実行する前にテーブルを切り捨てる方法はありますか?

データベースを開いて手動で実行できることは知っていますが、よりスマートな方法はありますか?

37
Nick

最も賢い方法は、おそらく型を変更しないことです。これを行う必要がある場合は、次の手順を実行することをお勧めします。

  1. 新しいタイプの新しい列を追加します
  2. Sql()を使用して、更新ステートメントを使用して元の列からデータを引き継ぎます
  3. 古い列を削除する
  4. 新しい列の名前を変更します

これはすべて同じ移行で実行でき、正しいSQLスクリプトが作成されます。データを破棄する場合は、手順2をスキップできます。引き継ぎたい場合は、適切なステートメントを追加します(switchステートメントを含めることもできます)。

残念ながら、Code First Migrationsはこれを実現する簡単な方法を提供していません。

コードの例を次に示します。

AddColumn("dbo.People", "LocationTmp", c => c.Int(nullable: false));
Sql(@"
    UPDATE dbp.People
    SET LocationTmp =
        CASE Location
            WHEN 'London' THEN 1
            WHEN 'Edinburgh' THEN 2
            WHEN 'Cardiff' THEN 3
            ELSE 0
        END
    ");
DropColumn("dbo.People", "Location");
RenameColumn("dbo.People", "LocationTmp", "Location");

@JustAnotherUserYouMayKnowの回答に基づいていますが、簡単です。

最初にSql()コマンドを実行してから、AlterColumn()コマンドを実行してください:

Sql(@"
    UPDATE dbo.People
    SET Location =
        CASE Location
            WHEN 'London' THEN 1
            WHEN 'Edinburgh' THEN 2
            WHEN 'Cardiff' THEN 3
            ELSE 0
        END
    ");
AlterColumn("dbo.People", "Location", c => c.Int(nullable: false));
17
Seven

これは質問には直接当てはまらないが、誰かに役立つ可能性があることは知っています。私の問題では、誤って年フィールドを日付時刻にし、すべてのデータを削除してからデータ型をintに切り替える方法を見つけようとしていました。

移行を追加するとき、EFは列を更新するだけでした。彼らがやりたいことを削除し、自分のコードを追加しなければなりませんでした。基本的に、列を削除して新しい列を追加しました。ここに私のために働いたものがあります。

protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.DropColumn(
            name: "TestingPeriodYear",
            table: "ControlActivityIssue");

        migrationBuilder.AddColumn<int>(
            name: "TestingPeriodYear",
            table: "ControlActivityIssue",
            nullable: true);
    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.DropColumn(
            name: "TestingPeriodYear",
            table: "ControlActivityIssue");

        migrationBuilder.AddColumn<DateTime>(
            name: "TestingPeriodYear",
            table: "ControlActivityIssue",
            nullable: true);
    }
1
Ben