我想使用代码优先迁移为现有表添加一个具有唯一索引的新列。由于表已有数据,因此在创建新列时,所有现有行都将设置为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
方法在每一行上执行更新语句,但我不知道是否这是可能的。
在添加唯一约束之前,有没有办法在迁移中单独更新所有现有行?
答案 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");