【问题标题】:EF5 Code First - Changing A Column Type With MigrationsEF5 Code First - 使用迁移更改列类型
【发布时间】:2013-01-28 00:46:05
【问题描述】:

我是 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));

但是当我运行 update-database 时出现错误

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

迁移中有没有办法在运行alter语句之前截断表?

我知道我可以打开数据库并手动操作,但有没有更聪明的方法?

【问题讨论】:

  • 我建议处于类似职位的开发人员在遵循此页面上的一些答案之前查看转换是否由实体框架自动处理。例如,我发现它可以在没有我帮助的情况下处理字符串和小数之间的转换,然后再返回 - Add-Migration 生成的迁移工作正常。显然,在将其应用于生产之前,请先在本地/测试数据库上试用!

标签: c# ef-code-first entity-framework-5 entity-framework-migrations


【解决方案1】:

最聪明的方法可能是不改变类型。如果您需要这样做,我建议您执行以下步骤:

  1. 使用您的新类型添加新列
  2. 使用Sql() 使用更新语句从原始列中接管数据
  3. 删除旧列
  4. 重命名新列

这都可以在同一个迁移中完成,将创建正确的 SQL 脚本。如果您希望丢弃数据,可以跳过第 2 步。如果你想接管它,添加适当的语句(也可以包含一个 switch 语句)。

不幸的是,Code First 迁移没有提供更简单的方法来实现这一点。

示例代码如下:

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");

【讨论】:

  • 快速说明:在迁移Down() 时,我发现使用临时列是唯一的方法。架构更新似乎在还包含Sql() 命令的事务中运行,因为从Integer 值返回到String 的列更新失败并显示相同的错误消息——这次是Sql() 而不是@ 987654328@。即使我们在AlterColumn() 之后运行Sql()
【解决方案2】:

基于@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));

【讨论】:

  • 我喜欢它胜过标记答案:它不需要临时列。
【解决方案3】:

我知道这并不直接适用于问题,但可能对某人有所帮助。在我的问题中,我不小心将年份字段设置为日期时间,我试图弄清楚如何删除所有数据,然后将数据类型切换为 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);
    }

【讨论】:

  • 无法在具有现有数据的系统上执行此操作
猜你喜欢
  • 1970-01-01
  • 2013-04-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多