在 Entity Framework Core 中重命名外键而不删除数据

Posted

技术标签:

【中文标题】在 Entity Framework Core 中重命名外键而不删除数据【英文标题】:Rename a foreign key in Entity Framework Core without dropping data 【发布时间】:2018-09-22 18:55:06 【问题描述】:

我有两个模型类:

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

但是,我得到:

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

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

【问题讨论】:

【参考方案1】:

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

解决方法需要以下步骤:

(1) 在重命名实体之前,使用ToTableHasColumnName fluent 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; 

你就完成了。

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

【讨论】:

好像我需要在每个具有SurveyId 外键的模型上方添加[Column("StudentSurveyId")]。我会尝试... 那行得通。然后我应用重命名重构 (resharper) 以在我的项目中将调查更改为 StudentSurvey。然后你的第 3 步没用;我的迁移代码中显示了 drop 和 create 语句。我忘了改变我的上下文:public DbSet&lt;StudentSurvey&gt; Surveys get; set; 需要变成public DbSet&lt;StudentSurvey&gt; StudentSurveys get; set; 然后我遇到了同样的问题。因为 StudentSurveys 必须是 StudentSurvey。事后看来,我应该在我的第一个注释中将[Table(StudentSurvey)] 复数化。我建议更新您的答案以考虑所有这些。 好的。至少这是一个很好的起点 :) 在我的测试中,我没有重命名 FK 列。并假设DbSet 重命名是“重命名”操作的一部分。反正现在答案更新了,是不是漏了什么? @PateeGutee 不幸的是,这似乎不适用于 EF6。 在 EF 核心 3 中:如果我不生成第二次(空)迁移,那么我仍然在 ModelSnapshot 中:b.Property("Old") .HasColumnName("New")跨度> 【参考方案2】:

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

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

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

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

【讨论】:

以上是关于在 Entity Framework Core 中重命名外键而不删除数据的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Entity Framework Core 中运行存储过程?

如何在 Entity Framework Core 中运行存储过程?

什么是自有实体?何时以及为什么在 Entity Framework Core 中使用 Owned Entity?

Entity Framework Core 中 TimeSpan 的总和

Entity Framework优化一:引发了“System.Data.Entity.Core.EntityCommandExecutionException”类型的异常

如何在 Entity Framework Core 2 中播种?