我首先使用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_Id
和Course_Id
。到现在为止还好。
现在我需要将Course
实体中的主键从int
更改为Guid
。
我将数据类型从Guid
更改为int
,并成功创建了新的迁移。但是当我尝试运行命令update-database
时,我的错误就在下面。
操作数类型冲突:uniqueidentifier与查询中的int不兼容
我不知道如何解决上述错误。
另外,我认为在不丢失数据的情况下更改主键字段的数据类型是不可能的(当然,我们可以手动从备份中复制)。我希望我错了。
有人可以告诉我有没有更好的方法来实现这一点而不会丢失数据?
注意:我已经浏览了其他相关论坛和其他互联网论坛,但没有运气。
答案 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
一起使用,但可能无法与其他数据库引擎一起使用。使用迁移而不是播种器是另一个缺点。