Entity Framework Migrations เปลี่ยนชื่อตารางและคอลัมน์


118

ฉันเปลี่ยนชื่อเอนทิตีสองรายการและคุณสมบัติการนำทางของพวกเขาและสร้างการย้ายใหม่ใน EF 5 ตามปกติกับการเปลี่ยนชื่อในการย้ายข้อมูล 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.ReportSectionsไปdbo.ReportPagesแล้วเพื่อdbo.ReportSectionGroups dbo.ReportSectionsแล้วผมก็ต้องเปลี่ยนชื่อคอลัมน์คีย์ต่างประเทศdbo.ReportPagesจากการGroup_IdSection_Id

ฉันวางคีย์ต่างประเทศและดัชนีที่เชื่อมตารางเข้าด้วยกันจากนั้นฉันกำลังเปลี่ยนชื่อตารางและคอลัมน์คีย์ต่างประเทศจากนั้นฉันจะเพิ่มดัชนีและคีย์ต่างประเทศอีกครั้ง ฉันคิดว่านี่จะใช้งานได้ แต่ฉันได้รับข้อผิดพลาด SQL

ข่าวสารเกี่ยวกับ 15248 ระดับ 11 สถานะ 1 ขั้นตอน sp_rename บรรทัด 215 พารามิเตอร์ @objname ไม่ชัดเจนหรือ @objtype (COLUMN) ที่อ้างสิทธิ์ไม่ถูกต้อง ข่าวสารเกี่ยวกับ 4902 ระดับ 16 สถานะ 1 บรรทัด 10 ไม่พบวัตถุ "dbo รายงานSections" เนื่องจากไม่มีอยู่หรือคุณไม่มีสิทธิ์

ฉันไม่มีเวลาง่ายๆที่จะหาว่าเกิดอะไรขึ้นที่นี่ ข้อมูลเชิงลึกใด ๆ จะเป็นประโยชน์อย่างมาก


ข้อใดล้มเหลว คุณสามารถติดตามการย้ายข้อมูลใน SQL Server Profiler และตรวจสอบ SQL ที่เกี่ยวข้องได้หรือไม่
Albin Sunnanbo

คำตอบ:


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_renameคำสั่ง T-SQL ที่ใช้parsenameภายในซึ่งมีข้อ จำกัด บางประการ ดังนั้นหากคุณมีชื่อตารางที่มีจุดอยู่เช่น "SubSystemA.Tablename" ให้ใช้:RenameColumn("dbo.[SubSystemA.Tablename]", "OldColumnName", "NewColumnName");
Ilan

10
ดูเหมือนว่าจะอัปเดตคอลัมน์ที่อ้างถึงใน Foreign Keys แต่ไม่ได้เปลี่ยนชื่อ FK เอง ซึ่งน่าเสียดาย แต่อาจจะไม่ใช่จุดจบของโลกเว้นแต่คุณจะต้องอ้างถึง FK ในภายหลังด้วยชื่อของมัน
mikesigs

9
@mikesigs คุณสามารถใช้RenameIndex(..)ในการโยกย้ายเพื่อเปลี่ยนชื่อได้
JoeBrockhaus

1
ฉันได้รับข้อยกเว้นเมื่อเปลี่ยนชื่อคอลัมน์ อาจเป็นเพราะยังไม่ได้ใช้ตารางการเปลี่ยนชื่อ ฉันต้องแบ่งมันออกเป็นสองการโยกย้าย
Josue Martinez

ด้วย EF6 ใช้RenameTable(..)เพื่อเปลี่ยนชื่อ FK และ PK ฟังดูไม่ถูกต้อง แต่เป็นสิ่งที่ได้ผลสำหรับฉัน เป็นวิธีการสร้าง T-SQL ( execute sp_rename ...) ที่ถูกต้อง หากคุณอัปเดตฐานข้อมูล -verbose คุณจะเห็นด้วยตัวคุณเอง
Giovanni

44

หากคุณไม่ชอบเขียน / เปลี่ยนรหัสที่ต้องการในคลาส Migration ด้วยตนเองคุณสามารถทำตามแนวทางสองขั้นตอนซึ่งจะทำให้ RenameColumnรหัสที่จำเป็น :

ขั้นตอนที่หนึ่งใช้ColumnAttributeเพื่อแนะนำชื่อคอลัมน์ใหม่จากนั้นเพิ่มการโยกย้าย (เช่นAdd-Migration ColumnChanged)

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

ขั้นตอนที่สองเปลี่ยนชื่อคุณสมบัติและใช้กับการย้ายข้อมูลเดียวกันอีกครั้ง (เช่นAdd-Migration ColumnChanged -force) ในคอนโซลตัวจัดการแพ็คเกจ

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

RenameColumnถ้าคุณดูที่ชั้นโยกย้ายคุณสามารถดูรหัสโดยอัตโนมัติที่สร้างขึ้นมี


คุณจะเพิ่มการย้ายข้อมูลเดิมสองครั้งได้อย่างไร เมื่อฉันลองสิ่งนี้ฉันจะได้รับ:The name 'Rename_SalesArea' is used by an existing migration.
Andrew S

ดู-forceพารามิเตอร์เมื่อใช้ add-migration
Hossein Narimani Rad

2
โปรดทราบว่าโพสต์นี้ไม่ได้มีไว้สำหรับ EF core
Hossein Narimani Rad

7
ฉันคิดว่าคุณต้องการการย้ายข้อมูลเพียงครั้งเดียว แต่ยังมีอีกสองขั้นตอน 1. เพิ่มแอตทริบิวต์และสร้าง "เปลี่ยนชื่อการย้ายข้อมูล" 2. เพียงแค่เปลี่ยนชื่อคุณสมบัติ แค่นั้นแหละ. ไม่ว่าจะด้วยวิธีใดสิ่งนี้ช่วยให้ฉันประหยัดเวลาได้มาก ขอบคุณ!
Crispy Ninja

1
ฉันทำตามขั้นตอนที่กล่าวถึงที่นี่และประสบความสำเร็จ ฉันไม่ได้สูญเสียข้อมูลที่มีอยู่ สิ่งที่ฉันต้องการจริงๆทำการเปลี่ยนแปลงโดยไม่สูญเสียข้อมูล แต่ฉันเรียกใช้การย้ายข้อมูลที่แตกต่างกันหลังจากเปลี่ยนชื่อคุณสมบัติของคลาสสำหรับด้านความปลอดภัย
Manojb86

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
ดูเหมือนว่าควรจะเป็นค่าเริ่มต้นในการเพิ่มแอตทริบิวต์ของตารางทำให้สิ่งต่างๆง่ายขึ้นมาก
patrick

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 มันเปลี่ยนชื่อตารางได้อย่างถูกต้อง (อย่าลืมเปลี่ยนชื่อDbSetในของคุณDatabaseContextด้วย) การเปลี่ยนคีย์หลักอาจทำให้เกิดปัญหา การย้ายข้อมูลจะพยายามลบและสร้างใหม่ ดังนั้นคุณต้องปรับเปลี่ยนและทำตามคำตอบของ 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");
            });
     }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.