在向代码首次迁移的现有行添加唯一索引时处理重复项

时间:2016-03-21 18:13:06

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

我想使用代码优先迁移为现有表添加一个具有唯一索引的新列。由于表已有数据,因此在创建新列时,所有现有行都将设置为NULL。迁移无法应用,因为这违反了新索引的唯一约束。

这是我的迁移:

public override void Up()
{
    AddColumn("dbo.SignInToken", "Token", c => c.String(maxLength: 32));
    // How can I update existing rows here?
    CreateIndex("dbo.SignInToken", "Token", unique: true);
}

错误:

  

CREATE UNIQUE INDEX语句终止,因为找到了对象名称的重复键' dbo.SignInToken'和索引名称' IX_Token'。重复键值为()。

新列将包含我在代码中设置的随机生成的字符串。

我知道我可以使用Sql方法在迁移中执行原始SQL。我想过使用游标来更新每一行,但是我必须编写一个存储过程来模仿生成随机令牌的C#方法。我宁愿不要这样做。我想到的另一件事是,如果有办法检索所有行,我可以循环遍历它们并使用Sql方法在每一行上执行更新语句,但我不知道是否这是可能的。

在添加唯一约束之前,有没有办法在迁移中单独更新所有现有行?

2 个答案:

答案 0 :(得分:4)

一种解决方案是将主键复制到新列中。如果有必要,可以在种子方法中更新它。

更新了迁移:

public override void Up()
{
    AddColumn("dbo.SignInToken", "Token", c => c.String(maxLength: 40));
    Sql("UPDATE dbo.SignInToken SET Token = Id");
    CreateIndex("dbo.SignInToken", "Token", unique: true);
}

然后,如果需要,您可以使用DbContext方法中的Seed来更新行。需要一些逻辑来确保它在将来的迁移中不会覆盖新数据(例如,只有在Id == Token时才更新行。)

答案 1 :(得分:0)

此外,如果您没有现有的唯一列(要复制它的值),并且想要为已经有一些行的表添加一个唯一列,您可以尝试这种方法

AddColumn("dbo.SignInToken", 
          "Token", 
          c => c.Int(nullable: false,
                     defaultValueSql: "CONVERT(int, CONVERT(VARBINARY(16), NEWID(), 1))"));

CreateIndex("dbo.IriSqlServerLayer", "Order", unique: true, name: "IX_UniqueOrder");