在Entity Framework Core中重命名外键而不丢失数据

26

我有两个模型类:

public class Survey
{
    public int SurveyId { get; set; }
    public string Name { get; set; }
}

public class User
{
    public int UserId { get; set; }

    public int SurveyId { get; set; }
    public Survey Survey { get; set; }
}

我想将Survey重命名为StudentSurvey,并将其具有StudentSurveyId。我相应地更新模型中的类名和属性,并添加了迁移。

然而,我遇到了以下问题:

与FOREIGN KEY约束“FK_User_Surveys_SurveyId”冲突的 ALTER TABLE 语句与数据库“AppName”中的表“dbo.Survey”列“SurveyId”发生冲突。

我认为它正在尝试删除数据,因为它需要列中的数据(不能为null),所以我看到这个错误。但我不想删除这些数据。我该如何仅仅进行重命名操作?

2个回答

76

EF Core将实体类重命名视为删除旧实体并添加新实体,因此会生成一个迁移来删除原始表并创建一个新表。

解决方法需要执行以下步骤:

(1) 在重命名实体之前,使用ToTableHasColumnName流畅API或数据注释来“重命名”表格和PK列。同时也要对引用该实体的FK列执行相同操作。

例如:

[Table("StudentSurveys")]
public class Survey
{
    [Column("StudentSurveyId")]
    public int SurveyId { get; set; }
    public string Name { get; set; }
}

public class User
{
    public int UserId { get; set; }
    [Column("StudentSurveyId")]
    public int SurveyId { get; set; }
    public Survey Survey { get; set; }
}

(2) 添加新迁移。它将正确重命名表、PK列、FK列和相关约束条件:

protected override void Up(MigrationBuilder migrationBuilder)
{
    migrationBuilder.DropForeignKey(
        name: "FK_Users_Surveys_SurveyId",
        table: "Users");

    migrationBuilder.DropPrimaryKey(
        name: "PK_Surveys",
        table: "Surveys");

    migrationBuilder.RenameTable(
        name: "Surveys",
        newName: "StudentSurveys");

    migrationBuilder.RenameColumn(
        name: "SurveyId",
        table: "Users",
        newName: "StudentSurveyId");

    migrationBuilder.RenameIndex(
        name: "IX_Users_SurveyId",
        table: "Users",
        newName: "IX_Users_StudentSurveyId");

    migrationBuilder.RenameColumn(
        name: "SurveyId",
        table: "StudentSurveys",
        newName: "StudentSurveyId");

    migrationBuilder.AddPrimaryKey(
        name: "PK_StudentSurveys",
        table: "StudentSurveys",
        column: "StudentSurveyId");

    migrationBuilder.AddForeignKey(
        name: "FK_Users_StudentSurveys_StudentSurveyId",
        table: "Users",
        column: "StudentSurveyId",
        principalTable: "StudentSurveys",
        principalColumn: "StudentSurveyId",
        onDelete: ReferentialAction.Cascade);
}

(3) 删除注释 / 流畅配置并进行实际的类/属性重命名:

public class StudentSurvey
{
    public int StudentSurveyId { get; set; }
    public string Name { get; set; }
}

public class User
{
    public int SurveyUserId { get; set; }
    public int StudentSurveyId { get; set; }
    public StudentSurvey StudentSurvey { get; set; }
}

如果有的话,请重命名相应的DbSet

public DbSet<StudentSurvey> StudentSurveys { get; set; }

完成了。

您可以通过添加新迁移来验证 - 它将具有空的 Up Down 方法。


好像我需要在每个具有SurveyId外键的模型上方添加[Column("StudentSurveyId")]。我会尝试一下... - egmfrs
1
成功了。然后我应用了重命名重构(resharper)将Survey更改为StudentSurvey在我的项目中。然后你的第三步没有起作用;删除和创建语句显示在我的迁移代码中。我忘记改变我的上下文:public DbSet<StudentSurvey> Surveys { get; set; }需要变成public DbSet<StudentSurvey> StudentSurveys { get; set; },然后我遇到了同样的问题。因为StudentSurveys需要变成StudentSurvey。事后看来,我应该在我的第一个注释中将[Table(StudentSurvey)]复数化。我建议更新你的答案考虑到所有这些。 - egmfrs
2
好的,至少这是一个不错的起点 :) 在我的测试中,我没有重命名FK列。并且假设DbSet重命名是“重命名”操作的一部分。无论如何,答案现在已经更新了,我有遗漏什么吗? - Ivan Stoev
很遗憾,似乎这对EF6不起作用。 - Ivan Stoev
2
在 EF Core 3 中:如果我不生成第二个(空的)迁移,则我仍然在 ModelSnapshot 中有:b.Property<string>("Old").HasColumnName("New")。 - zolty13

2

文档建议使用MigrationBuilder.RenameColumn()。还有RenameIndex()RenameTable()和一个存储过程'sp_rename'(在SQL Server中)用于重命名外键。例如:

migrationBuilder.Sql($"EXEC sp_rename 'dbo.{oldName}', '{newName}'");

使用SQL Server Management Studio中的数据库脚本工具(右键单击DB | 任务 | 生成脚本...)生成模式脚本,包括和不包括EF迁移自定义,以进行比较。然后您将知道EF命名是否得到保留。
编辑:添加“EXEC ...”以支持生成独立的T-SQL脚本。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接