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_Idв Section_Id.

Я удаляю внешние ключи и индексы, связывающие таблицы вместе, затем переименовываю таблицы и столбец внешнего ключа, затем снова добавляю индексы и внешние ключи. Я предполагал, что это сработает, но получаю ошибку SQL.

Msg 15248, уровень 11, состояние 1, процедура sp_rename, строка 215 Либо параметр @objname неоднозначен, либо заявленный @objtype (COLUMN) неверен. Msg 4902, уровень 16, состояние 1, строка 10 Не удается найти объект «dbo.ReportSections», поскольку он не существует или у вас нет разрешений.

Мне нелегко понять, что здесь не так. Любое понимание было бы чрезвычайно полезно.

Чев
источник
Какая из вышеперечисленных строк не работает? Можете ли вы отследить миграцию в SQL Server Profiler и проверить соответствующий 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_renameT-SQL, который использует parsenameвнутреннее использование, что имеет некоторые ограничения. Поэтому, если у вас есть имя таблицы, в котором есть точки, например "SubSystemA.Tablename", используйте:RenameColumn("dbo.[SubSystemA.Tablename]", "OldColumnName", "NewColumnName");
Ilan
10
Кажется, что это обновляет столбцы, указанные во внешних ключах, но не переименовывает сам FK. Это позор, но, вероятно, это не конец света, если только вам не понадобится позже ссылаться на FK по его имени.
mikesigs
9
@mikesigs, который вы можете использовать RenameIndex(..)при миграции, чтобы переименовать
JoeBrockhaus
1
Я получаю исключение при переименовании столбца. вероятно, потому что таблица переименования все еще не применяется. Мне пришлось разделить это на две миграции
Хосуэ Мартинес
С EF6 используйте RenameTable(..)для переименования FK и PK. Звучит неправильно, но у меня это сработало. Это метод, который создает правильный T-SQL ( execute sp_rename ...). Если вы выполните update-database -verbose, вы убедитесь в этом сами.
Джованни,
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) в консоли диспетчера пакетов.

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.
Эндрю С.
взгляните на -forceпараметр при использовании добавления-миграции
Хоссейн Наримани Рад
2
также обратите внимание, что этот пост не для ядра EF
Хоссейн Наримани Рад
7
Я думаю, вам понадобится всего одна миграция, но все же два шага. 1. Добавьте атрибут и создайте «переименование миграции» 2. Просто измените имя свойства. Вот и все. В любом случае, это сэкономило мне массу времени. Спасибо!
Crispy Ninja
1
Я выполнил шаги, упомянутые здесь, и все прошло успешно. Я не потерял никаких существующих данных. то, что я действительно хотел, внести изменения без потери данных. Но на всякий случай я запускаю другую миграцию после переименования имени свойства класса.
Manojb86 08
19

Чтобы немного расширить ответ Хосейна Наримани Рэда, вы можете переименовать как таблицу, так и столбцы, используя 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");
    }
mirind4
источник
3

В ef core вы можете изменить миграцию, созданную после добавления миграции. А потом сделайте update-database. Образец приведен ниже:

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 (всегда) мог обнаруживать такие простые миграции.

naskew
источник
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");
            });
     }
}
Мартин Штауфчик
источник