Перейменування таблиць і стовпців міграцій Entity Framework


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_Idдо до Section_Id.

Я скидаю зовнішні ключі та індекси, що пов'язують таблиці разом, потім я перейменую таблиці та стовпець із зовнішнім ключем, потім знову додаю індекси та зовнішні ключі. Я припускав, що це буде працювати, але я отримую помилку SQL.

Msg 15248, рівень 11, стан 1, процедура sp_rename, рядок 215 Або параметр @objname неоднозначний, або заявлений @objtype (COLUMN) невірний. Msg 4902, рівень 16, стан 1, рядок 10 Неможливо знайти об’єкт "dbo.ReportSections", оскільки він не існує або у вас немає дозволів.

Мені непросто зрозуміти, що тут не так. Будь-яке розуміння було б надзвичайно корисним.


Який із наведених рядків не вдається? Чи можете ви простежити міграцію в Profiler SQL Server і перевірити відповідний SQL?
— Альбін Суннанбо

Відповіді:


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. Це соромно, але, мабуть, не кінець світу, якщо вам абсолютно не потрібно посилатися на ФК пізніше за його назвою.
— mikesigs

9
@mikesigs ви можете використовувати RenameIndex(..)для міграції, щоб перейменувати її
— JoeBrockhaus

1
Я отримую виняток під час перейменування стовпця. ймовірно, тому, що таблиця перейменування досі не застосовується. Мені довелося розділити це на дві міграції
— Джозе Мартінес

За допомогою EF6 використовуйте RenameTable(..)для перейменування ФК та ​​ПК. Це не правильно, але це те, що працювало для мене. Це метод, який створює правильний T-SQL ( execute sp_rename ...). Якщо ви зробите update-database -verbose, ви побачите це самі.
— Джованні

44

Якщо вам не подобається писати / змінювати необхідний код у класі міграції вручну, ви можете дотримуватися двоетапного підходу, який автоматично зробить необхідний 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.
— Ендрю S,

подивіться на -forceпараметр під час використання додаткової міграції
— Hossein Narimani Rad 13.03.18

2
також зверніть увагу, що ця публікація не є основою EF
— Hossein Narimani Rad

6
Я думаю, вам потрібна лише одна міграція, але все-таки два кроки. 1. Додайте атрибут та створіть "перейменування міграції" 2. Просто змініть ім'я властивості. Це воно. Так чи інакше, це просто заощадило мені тону часу. Дякую!
— Хрусткий ніндзя

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
Здається, що за замовчуванням слід додати атрибут таблиці, робить речі набагато простішими.
— патрік

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 ви можете змінити міграцію, створену після додавання міграції. А потім зробіть update-базу даних. Зразок наведено нижче:

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 (перейменування першої сутності коду). Я просто перейменував клас і додав міграцію за допомогою консолі менеджера пакунків та вуаля, міграція за допомогою RenameTable (...) автоматично генерується для мене. Я повинен визнати, що я переконався, що єдиною зміною сутності було його перейменування, тому немає нових стовпців чи перейменованих стовпців, тому я не можу бути впевнений, чи це справа EF6, чи просто EF (завжди) змогла виявити такі прості міграції.


2
Я можу підтвердити це за допомогою 6.1.3. Він правильно перейменовує таблицю (не забудьте перейменувати також і DbSetваш DatabaseContext). Зміна первинного ключа викликає неполадки. Міграція спробує її видалити та створити нову. Тому вам потрібно відрегулювати це і зробити так, як відповідь Шева, перейменуйте стовпець.
— 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");
            });
     }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.