实体框架迁移重命名表和列


118

我重命名了几个实体及其导航属性,并在EF 5中生成了一个新的Migration。与EF迁移中的重命名一样,默认情况下,它将删除对象并重新创建它们。那不是我想要的,所以我几乎不得不从头开始构建迁移文件。

    public override void Up()
    {
        DropForeignKey("dbo.ReportSectionGroups", "Report_Id", "dbo.Reports");
        DropForeignKey("dbo.ReportSections", "Group_Id", "dbo.ReportSectionGroups");
        DropForeignKey("dbo.Editables", "Section_Id", "dbo.ReportSections");
        DropIndex("dbo.ReportSectionGroups", new[] { "Report_Id" });
        DropIndex("dbo.ReportSections", new[] { "Group_Id" });
        DropIndex("dbo.Editables", new[] { "Section_Id" });

        RenameTable("dbo.ReportSections", "dbo.ReportPages");
        RenameTable("dbo.ReportSectionGroups", "dbo.ReportSections");
        RenameColumn("dbo.ReportPages", "Group_Id", "Section_Id");

        AddForeignKey("dbo.ReportSections", "Report_Id", "dbo.Reports", "Id");
        AddForeignKey("dbo.ReportPages", "Section_Id", "dbo.ReportSections", "Id");
        AddForeignKey("dbo.Editables", "Page_Id", "dbo.ReportPages", "Id");
        CreateIndex("dbo.ReportSections", "Report_Id");
        CreateIndex("dbo.ReportPages", "Section_Id");
        CreateIndex("dbo.Editables", "Page_Id");
    }

    public override void Down()
    {
        DropIndex("dbo.Editables", "Page_Id");
        DropIndex("dbo.ReportPages", "Section_Id");
        DropIndex("dbo.ReportSections", "Report_Id");
        DropForeignKey("dbo.Editables", "Page_Id", "dbo.ReportPages");
        DropForeignKey("dbo.ReportPages", "Section_Id", "dbo.ReportSections");
        DropForeignKey("dbo.ReportSections", "Report_Id", "dbo.Reports");

        RenameColumn("dbo.ReportPages", "Section_Id", "Group_Id");
        RenameTable("dbo.ReportSections", "dbo.ReportSectionGroups");
        RenameTable("dbo.ReportPages", "dbo.ReportSections");

        CreateIndex("dbo.Editables", "Section_Id");
        CreateIndex("dbo.ReportSections", "Group_Id");
        CreateIndex("dbo.ReportSectionGroups", "Report_Id");
        AddForeignKey("dbo.Editables", "Section_Id", "dbo.ReportSections", "Id");
        AddForeignKey("dbo.ReportSections", "Group_Id", "dbo.ReportSectionGroups", "Id");
        AddForeignKey("dbo.ReportSectionGroups", "Report_Id", "dbo.Reports", "Id");
    }

我要做的就是重命名dbo.ReportSectionsdbo.ReportPages,然后命名dbo.ReportSectionGroupsdbo.ReportSections。然后,我需要外键列上的重命名dbo.ReportPages,从Group_IdSection_Id

我删除将表链接在一起的外键和索引,然后重命名表和外键列,然后再次添加索引和外键。我以为这是行得通的,但是我遇到了SQL错误。

消息15248,级别11,状态1,过程sp_rename,行215参数@objname含糊或声明的@objtype(COLUMN)错误。消息4902,级别16,状态1,行10找不到对象“ dbo.ReportSections”,因为它不存在或您没有权限。

我没有轻松的时间弄清楚这里出了什么问题。任何见解将大有帮助。


以上哪几行失败?您可以在SQL Server Profiler中跟踪迁移并检查相应的SQL吗?
Albin Sunnanbo 2012年

Answers:


143

没关系。我使这种方式变得比实际需要的更加复杂。

这就是我所需要的。重命名方法只是生成对sp_rename系统存储过程的调用,我想它已经处理了所有事情,包括带有新列名的外键。

public override void Up()
{
    RenameTable("ReportSections", "ReportPages");
    RenameTable("ReportSectionGroups", "ReportSections");
    RenameColumn("ReportPages", "Group_Id", "Section_Id");
}

public override void Down()
{
    RenameColumn("ReportPages", "Section_Id", "Group_Id");
    RenameTable("ReportSections", "ReportSectionGroups");
    RenameTable("ReportPages", "ReportSections");
}

29
注意表名中带有点。RenameColumn生成一条sp_renameT-SQL语句,该语句在parsename内部使用use,这有一些限制。因此,如果您的表名中带有点,例如“ SubSystemA.Tablename”,则使用:RenameColumn("dbo.[SubSystemA.Tablename]", "OldColumnName", "NewColumnName");
Ilan,

10
这似乎可以更新外键中引用的列,但不会重命名FK本身。除非您在以后绝对需要使用FK的名称来引用它,否则这很遗憾,但可能不是世界末日。
mikesigs 2014年

9
您可以RenameIndex(..)在迁移中使用@mikesigs 重命名它
JoeBrockhaus

1
重命名列时出现异常。可能是因为仍未应用重命名表。我不得不把它分成两个迁移
约瑟·马丁内斯

对于EF6,用于RenameTable(..)重命名FK和PK。听起来不对,但这对我有用。这是创建正确的T-SQL(execute sp_rename ...)的方法。如果您执行update-database -verbose,您将自己看到它。
Giovanni

44

如果您不喜欢在Migration类中手动编写/更改所需的代码,则可以按照两步方法自动RenameColumn生成所需的代码:

步骤1使用ColumnAttribute引入新的列名称,然后添加迁移(例如Add-Migration ColumnChanged

public class ReportPages
{
    [Column("Section_Id")]                 //Section_Id
    public int Group_Id{get;set}
}

第二步,更改属性名称,然后再次Add-Migration ColumnChanged -force在Package Manager控制台中将其应用于相同的迁移(例如)

public class ReportPages
{
    [Column("Section_Id")]                 //Section_Id
    public int Section_Id{get;set}
}

如果查看Migration类,您会看到自动生成的代码是RenameColumn


您如何能够两次添加相同的迁移?当我尝试此操作时,我得到:The name 'Rename_SalesArea' is used by an existing migration.
Andrew S

看一下-force使用添加迁移时的参数
Hossein Narimani Rad

2
也要注意这个帖子是不是EF核心
侯赛因Narimani Rad公司

6
我认为您只需要一次迁移,但仍然需要两步。1.添加属性并创建“重命名迁移”。2.只需更改属性名称。而已。无论哪种方式,这都节省了我很多时间。谢谢!
脆皮忍者'18

1
我按照此处提到的步骤进行操作,并且成功。我没有丢失任何现有数据。我真正想要的是进行更改而不会丢失数据。但是为了安全起见,我在重命名类的属性名称后运行了不同的迁移。
Manojb86 '19

19

要扩展Hossein Narimani Rad的答案,可以分别使用System.ComponentModel.DataAnnotations.Schema.TableAttribute和System.ComponentModel.DataAnnotations.Schema.ColumnAttribute来重命名表和列。

这有几个好处:

  1. 这不仅会自动创建名称迁移,而且还会自动迁移
  2. 它还会美味地删除所有外键,并根据新的表名和列名重新创建它们,从而为外键提供适当的名称。
  3. 所有这些都不会丢失任何表数据

例如,添加[Table("Staffs")]

[Table("Staffs")]
public class AccountUser
{
    public long Id { get; set; }

    public long AccountId { get; set; }

    public string ApplicationUserId { get; set; }

    public virtual Account Account { get; set; }

    public virtual ApplicationUser User { get; set; }
}

将产生迁移:

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

        migrationBuilder.DropForeignKey(
            name: "FK_AccountUsers_AspNetUsers_ApplicationUserId",
            table: "AccountUsers");

        migrationBuilder.DropPrimaryKey(
            name: "PK_AccountUsers",
            table: "AccountUsers");

        migrationBuilder.RenameTable(
            name: "AccountUsers",
            newName: "Staffs");

        migrationBuilder.RenameIndex(
            name: "IX_AccountUsers_ApplicationUserId",
            table: "Staffs",
            newName: "IX_Staffs_ApplicationUserId");

        migrationBuilder.RenameIndex(
            name: "IX_AccountUsers_AccountId",
            table: "Staffs",
            newName: "IX_Staffs_AccountId");

        migrationBuilder.AddPrimaryKey(
            name: "PK_Staffs",
            table: "Staffs",
            column: "Id");

        migrationBuilder.AddForeignKey(
            name: "FK_Staffs_Accounts_AccountId",
            table: "Staffs",
            column: "AccountId",
            principalTable: "Accounts",
            principalColumn: "Id",
            onDelete: ReferentialAction.Cascade);

        migrationBuilder.AddForeignKey(
            name: "FK_Staffs_AspNetUsers_ApplicationUserId",
            table: "Staffs",
            column: "ApplicationUserId",
            principalTable: "AspNetUsers",
            principalColumn: "Id",
            onDelete: ReferentialAction.Restrict);
    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.DropForeignKey(
            name: "FK_Staffs_Accounts_AccountId",
            table: "Staffs");

        migrationBuilder.DropForeignKey(
            name: "FK_Staffs_AspNetUsers_ApplicationUserId",
            table: "Staffs");

        migrationBuilder.DropPrimaryKey(
            name: "PK_Staffs",
            table: "Staffs");

        migrationBuilder.RenameTable(
            name: "Staffs",
            newName: "AccountUsers");

        migrationBuilder.RenameIndex(
            name: "IX_Staffs_ApplicationUserId",
            table: "AccountUsers",
            newName: "IX_AccountUsers_ApplicationUserId");

        migrationBuilder.RenameIndex(
            name: "IX_Staffs_AccountId",
            table: "AccountUsers",
            newName: "IX_AccountUsers_AccountId");

        migrationBuilder.AddPrimaryKey(
            name: "PK_AccountUsers",
            table: "AccountUsers",
            column: "Id");

        migrationBuilder.AddForeignKey(
            name: "FK_AccountUsers_Accounts_AccountId",
            table: "AccountUsers",
            column: "AccountId",
            principalTable: "Accounts",
            principalColumn: "Id",
            onDelete: ReferentialAction.Cascade);

        migrationBuilder.AddForeignKey(
            name: "FK_AccountUsers_AspNetUsers_ApplicationUserId",
            table: "AccountUsers",
            column: "ApplicationUserId",
            principalTable: "AspNetUsers",
            principalColumn: "Id",
            onDelete: ReferentialAction.Restrict);
    }

1
似乎应该将它作为添加table属性的默认值,这使事情变得更加简单。
帕特里克

17

在EF Core中,我使用以下语句重命名表和列:

至于重命名表:

    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.RenameTable(name: "OldTableName", schema: "dbo", newName: "NewTableName", newSchema: "dbo");
    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.RenameTable(name: "NewTableName", schema: "dbo", newName: "OldTableName", newSchema: "dbo");
    }

至于重命名列:

    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.RenameColumn(name: "OldColumnName", table: "TableName", newName: "NewColumnName", schema: "dbo");
    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.RenameColumn(name: "NewColumnName", table: "TableName", newName: "OldColumnName", schema: "dbo");
    }

3

在ef core中,您可以更改添加迁移后创建的迁移。然后做更新数据库。示例如下:

protected override void Up(MigrationBuilder migrationBuilder)
{
    migrationBuilder.RenameColumn(name: "Type", table: "Users", newName: "Discriminator", schema: "dbo");
}

protected override void Down(MigrationBuilder migrationBuilder)
{            
    migrationBuilder.RenameColumn(name: "Discriminator", table: "Users", newName: "Type", schema: "dbo");
}

2

我只是在EF6中尝试过相同的操作(代码重命名实体)。我只是简单地重命名了该类,并使用程序包管理器控制台和voila添加了迁移,因此自动为我生成了使用RenameTable(...)的迁移。我必须承认,我确保对实体的唯一更改是重命名它,所以没有新的列或重命名的列,因此我无法确定这是EF6还是EF(始终)能够检测到这种简单的迁移。


2
我可以证实这一点与6.1.3它正确地重命名表(不要忘了重命名DbSetDatabaseContext为好)。更改主键确实会引起麻烦。迁移将尝试删除它并创建一个新的。因此,您需要调整该值并按照Chev的答案进行操作,重命名该列。
CularBytes

1

可以将表名和列名指定为的映射的一部分DbContext。这样就无需在迁移中这样做。

public class MyContext : DbContext
{
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Restaurant>()
            .HasMany(p => p.Cuisines)
            .WithMany(r => r.Restaurants)
            .Map(mc =>
            {
                mc.MapLeftKey("RestaurantId");
                mc.MapRightKey("CuisineId");
                mc.ToTable("RestaurantCuisines");
            });
     }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.