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