Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rename a foreign key in Entity Framework Core without dropping data

I have two model 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; }
}

I want to rename Survey to StudentSurvey, so it will have StudentSurveyId. I update the class name and the properties in the models accordingly and add a migration.

However, I get:

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'.

I think it is trying to drop the data and because it requires data in the column (cannot be null) I'm seeing that error. But I don't want to drop the data. How can I just rename it?

like image 987
egmfrs Avatar asked Apr 12 '18 14:04

egmfrs


2 Answers

EF Core treats the entity class rename as deleting the old entity and adding a new entity, hence generates a migration to drop the original table and create a new one.

The workaround requires the following steps:

(1) Before renaming the entity, "rename" the table and the PK column by using either ToTable and HasColumnName fluent API or data annotations. Also do the same for FK columns referencing the entity.

For instance:

[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) Add new migration. It will correctly rename the table, PK column, FK columns and the associated constraints:

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) Remove the annotations / fluent configuration and do the actual class/property rename:

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

rename the corresponding DbSet if any:

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

and you are done.

You can verify that by adding a new migration - it will have empty Up and Down methods.

like image 113
Ivan Stoev Avatar answered Oct 21 '22 22:10

Ivan Stoev


The documentation suggests using MigrationBuilder.RenameColumn(). There is also RenameIndex(), RenameTable() and a stored procedure 'sp_rename' (in SQL Server) for renaming foreign keys. E.g:

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

Use the database scripting tool in SQL Server Management Studio (rt-click on DB | Tasks | Generate Scripts...) to generate schema scripts, with and without EF migration customizations, that can be compared. Then you'll know the EF naming is preserved.

EDIT: Added 'EXEC ...' to support generation of standalone T-SQL scripts

like image 1
Peter L Avatar answered Oct 21 '22 23:10

Peter L