在生产数据库中更改主键数据类型

时间:2016-11-09 17:30:24

标签: c# asp.net-mvc ef-code-first entity-framework-6 ef-migrations

我首先使用EF代码。我需要更改生产数据库中一个表的主键的数据类型。

public class Course 
{
   [Key]
   public Guid Id {get; set;}    //This needs to be changed to int

   public string Name {get;set;}

   public virtual ICollection<Group> Groups {get;set;}
}

public class Group
{
   [Key]
   public Guid Id {get; set;}

   public string Name {get;set;}

   public virtual ICollection<Course> Courses{get;set;}
}

由于以上是多人关系,EF已经自动创建了一个联接表GroupCourses与PK&amp; FK同时为Group_IdCourse_Id。到现在为止还好。

现在我需要将Course实体中的主键从int更改为Guid

我将数据类型从Guid更改为int,并成功创建了新的迁移。但是当我尝试运行命令update-database时,我的错误就在下面。

  

操作数类型冲突:uniqueidentifier与查询中的int不兼容

我不知道如何解决上述错误。

另外,我认为在不丢失数据的情况下更改主键字段的数据类型是不可能的(当然,我们可以手动从备份中复制)。我希望我错了。

有人可以告诉我有没有更好的方法来实现这一点而不会丢失数据?

注意:我已经浏览了其他相关论坛和其他互联网论坛,但没有运气。

1 个答案:

答案 0 :(得分:2)

您收到类型冲突错误,因为生成的迁移会尝试在受影响的列上直接ALTER COLUMN将类型从Guid更改为integer,但这是不可能的,因为Guid值不能隐式转换为integer值。

您必须手动修改迁移。删除AlterColumn行。为了在不丢失数据的情况下迁移现有的课程ID,您可以暂时重命名现有的Guid列(添加后缀_Old),使用正确的新名称和类型创建新列,填写内容使用带有UPDATE的{​​{1}}的正确值的关系表,最后删除旧列:

SELECT

我在代码中标记了我更改的行public override void Up() { DropForeignKey("dbo.GroupCourses", "Course_Id", "dbo.Courses"); DropIndex("dbo.GroupCourses", new[] { "Course_Id" }); DropPrimaryKey("dbo.Courses"); DropPrimaryKey("dbo.GroupCourses"); //Removed: AlterColumn("dbo.Courses", "Id", c => c.Int(nullable: false, identity: true)); RenameColumn("dbo.Courses", "Id", "Id_Old"); //Added AddColumn("dbo.Courses", "Id", c => c.Int(nullable: false, identity: true)); //Added //Removed: AlterColumn("dbo.GroupCourses", "Course_Id", c => c.Int(nullable: false)); RenameColumn("dbo.GroupCourses", "Course_Id", "Course_Id_Old"); //Added AddColumn("dbo.GroupCourses", "Course_Id", c => c.Int(nullable: false)); //Added Sql(@"UPDATE gc SET gc.Course_Id = c.Id " + "FROM dbo.GroupCourses as gc " + "INNER JOIN dbo.Courses as c ON gc.Course_Id_Old = c.id_Old"); //Added DropColumn("dbo.GroupCourses", "Course_Id_Old"); //Added DropColumn("dbo.Courses", "Id_Old"); //Added AddPrimaryKey("dbo.Courses", "Id"); AddPrimaryKey("dbo.GroupCourses", new[] { "Group_Id", "Course_Id" }); CreateIndex("dbo.GroupCourses", "Course_Id"); AddForeignKey("dbo.GroupCourses", "Course_Id", "dbo.Courses", "Id", cascadeDelete: true); } //Added。在将模型中的属性类型更改为//Removed后,代码的其余部分是迁移中的原始代码,该代码是使用Add-Migration生成的。

您只需在关系表的列中填入int个值即可。 UPDATE列中的值将自动生成,因为它是Courses.Id列。

您还应该找出identity方法并实现相反的过程,或者如果您真的永远不会将数据库降级到以前的迁移,请保持原样。

您也可以通过在迁移和播种器之间拆分代码来完成此操作。从概念上讲它会更正确,因为您应该使用迁移进行模式修改,并使用播种器进行数据修改。但我认为这样更容易,而且只需要一次迁移,而使用播种方法则需要进行两次迁移:一次包含上面包含的所有代码({1}}除外的句子和{{1}列,以及另一个只删除Down()列的列。 UPDATE部分将在播种机中实施。

警告:_Old语句中包含的SQL代码与_Old一起使用,但可能无法与其他数据库引擎一起使用。使用迁移而不是播种器是另一个缺点。