在不删除数据的情况下重命名 Entity Framework 核心中的外键

Rename a foreign key in Entity Framework Core without dropping data

我有两个模型 classes:

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。我相应地更新了模型中的 class 名称和属性并添加了迁移。

但是,我得到:

The ALTER TABLE statement conflicted with the FOREIGN KEY constraint "FK_User_Surveys_SurveyId". The conflict occurred in database "AppName", table "dbo.Survey", column 'SurveyId'.

我认为它正在尝试删除数据,因为它需要列中的数据(不能为空),所以我看到了该错误。但我不想删除数据。我怎样才能重命名它?

EF Core 将实体 class 重命名视为删除旧实体并添加新实体,因此生成迁移以删除原始 table 并创建新实体。

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

(1) 在重命名实体之前,"rename" table 和 PK 列使用 ToTableHasColumnName fluent API 或数据注释。也对引用实体的 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) 添加新迁移。它将正确重命名 table、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) 删除注释/流畅的配置并进行实际的 class/property 重命名:

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; }

大功告成。

您可以通过添加新的迁移来验证这一点 - 它会有空的 UpDown 方法。

documentation suggests using MigrationBuilder.RenameColumn(). There is also RenameIndex(), RenameTable() and a stored procedure 'sp_rename'(在 SQL 服务器中)用于重命名外键。例如:

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

使用 SQL Server Management Studio 中的数据库脚本工具(rt-click on DB | Tasks | Generate Scripts...)生成模式脚本,有和没有 EF 迁移定制,可以进行比较。然后你就会知道 EF 命名被保留了。

编辑: 添加了 'EXEC ...' 以支持生成独立的 T-SQL 脚本