Entity Framework Migrations mengganti nama tabel dan kolom

118

Saya mengganti nama beberapa entitas dan properti navigasinya dan menghasilkan Migrasi baru di EF 5. Seperti biasa dengan penggantian nama dalam migrasi EF, secara default akan menghapus objek dan membuatnya kembali. Bukan itu yang saya inginkan jadi saya harus membuat file migrasi dari awal.

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

Yang saya coba lakukan hanyalah mengganti nama dbo.ReportSectionsmenjadi dbo.ReportPagesdan kemudian dbo.ReportSectionGroupsmenjadi dbo.ReportSections. Lalu saya perlu mengganti nama kolom kunci asing dbo.ReportPagesdari Group_Idmenjadi Section_Id.

Saya membuang kunci asing dan indeks yang menghubungkan tabel bersama-sama, lalu saya mengganti nama tabel dan kolom kunci asing, lalu saya menambahkan indeks dan kunci asing lagi. Saya berasumsi ini akan berhasil tetapi saya mendapatkan kesalahan SQL.

Psn 15248, Level 11, Status 1, Prosedur sp_rename, Baris 215 Entah parameter @objname ambigu atau @objtype (COLUMN) yang diklaim salah. Psn 4902, Level 16, Status 1, Baris 10 Tidak dapat menemukan objek "dbo.ReportSections" karena tidak ada atau Anda tidak memiliki izin.

Saya tidak merasa mudah mencari tahu apa yang salah di sini. Wawasan apa pun akan sangat membantu.

Chev
sumber
Manakah dari baris di atas yang gagal? Dapatkah Anda melacak migrasi di SQL Server Profiler dan memeriksa SQL yang sesuai?
Albin Sunnanbo

Jawaban:

143

Lupakan. Saya membuat cara ini lebih rumit dari yang seharusnya.

Hanya ini yang saya butuhkan. Metode ganti nama hanya menghasilkan panggilan ke prosedur tersimpan sistem sp_rename dan saya rasa itu mengurus semuanya, termasuk kunci asing dengan nama kolom baru.

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");
}
Chev
sumber
29
Hati-hati dengan nama tabel yang memiliki titik di dalamnya. RenameColumnmenghasilkan pernyataan sp_renameT-SQL yang menggunakan penggunaan parsenameinternal yang memiliki beberapa batasan. Jadi jika Anda memiliki nama tabel yang memiliki titik-titik di dalamnya misalnya "SubSystemA.Tablename" maka gunakan:RenameColumn("dbo.[SubSystemA.Tablename]", "OldColumnName", "NewColumnName");
Ilan
10
Ini sepertinya memperbarui kolom yang direferensikan di Foreign Keys, tetapi tidak mengganti nama FK itu sendiri. Yang memalukan, tapi mungkin bukan akhir dari dunia kecuali Anda benar-benar perlu merujuk ke FK nanti dengan namanya.
mikesigs
9
@mikesigs yang dapat Anda gunakan RenameIndex(..)dalam migrasi untuk mengganti namanya
JoeBrockhaus
1
Saya mendapatkan pengecualian saat mengganti nama kolom. mungkin karena tabel ganti nama masih belum diterapkan. Saya harus membaginya menjadi dua migrasi
Josue Martinez
Dengan EF6, gunakan RenameTable(..)untuk mengganti nama FK dan PK. Kedengarannya tidak benar, tapi itulah yang berhasil bagi saya. Ini adalah metode yang membuat T-SQL ( execute sp_rename ...) yang benar. Jika Anda melakukan update-database -verbose, Anda akan melihatnya sendiri.
Giovanni
44

Jika Anda tidak suka menulis / mengubah kode yang diperlukan di kelas Migrasi secara manual, Anda dapat mengikuti pendekatan dua langkah yang secara otomatis membuat RenameColumnkode yang diperlukan:

Langkah Pertama Gunakan ColumnAttributeuntuk memperkenalkan nama kolom baru dan kemudian tambahkan migrasi (mis. Add-Migration ColumnChanged)

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

Langkah-Kedua, ubah nama properti dan terapkan lagi untuk migrasi yang sama (mis. Add-Migration ColumnChanged -force) Di Konsol Pengelola Paket

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

Jika Anda melihat kelas Migrasi, Anda dapat melihat kode yang dibuat secara otomatis adalah RenameColumn.

Hossein Narimani Rad
sumber
Bagaimana Anda dapat menambahkan migrasi yang sama dua kali? Ketika saya mencoba ini, saya mendapatkan:The name 'Rename_SalesArea' is used by an existing migration.
Andrew S
lihat -forceparameter saat menggunakan migrasi tambahan
Hossein Narimani Rad
2
juga perhatikan posting ini bukan untuk inti EF
Hossein Narimani Rad
6
Saya pikir Anda hanya perlu satu migrasi, tetapi masih dua langkah. 1. Tambahkan atribut dan buat "ganti nama migrasi" 2. Ubah saja nama properti. Itu dia. Bagaimanapun, ini hanya menghemat banyak waktu. Terima kasih!
Ninja Crispy
1
Saya mengikuti langkah-langkah yang disebutkan di sini dan berhasil. Saya tidak kehilangan data yang ada. itulah yang sebenarnya saya inginkan, lakukan perubahan tanpa kehilangan data. Tapi saya menjalankan migrasi yang berbeda setelah mengganti nama nama properti kelas, untuk sisi aman.
Manojb86
19

Untuk memperluas sedikit jawaban Hossein Narimani Rad, Anda dapat mengganti nama tabel dan kolom menggunakan System.ComponentModel.DataAnnotations.Schema.TableAttribute dan System.ComponentModel.DataAnnotations.Schema.ColumnAttribute masing-masing.

Ini memiliki beberapa manfaat:

  1. Ini tidak hanya akan membuat migrasi nama secara otomatis, tetapi
  2. itu juga akan menghapus kunci asing dan membuatnya kembali pada tabel baru dan nama kolom, memberikan kunci asing dan nama konstanta yang tepat.
  3. Semua ini tanpa kehilangan data tabel apa pun

Misalnya, menambahkan [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; }
}

Akan menghasilkan migrasi:

    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);
    }
Etienne Morin
sumber
1
Sepertinya itu harus menjadi default untuk menambahkan atribut tabel, membuatnya lebih sederhana.
patrick
17

Di EF Core, saya menggunakan pernyataan berikut untuk mengganti nama tabel dan kolom:

Adapun untuk mengganti nama tabel:

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

Adapun untuk mengganti nama kolom:

    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
sumber
3

Intinya, Anda dapat mengubah migrasi yang dibuat setelah menambahkan migrasi. Kemudian lakukan update-database. Contoh telah diberikan di bawah ini:

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");
}
Abdus Salam Azad
sumber
2

Saya baru saja mencoba yang sama di EF6 (ganti nama entitas pertama kode). Saya hanya mengganti nama kelas dan menambahkan migrasi menggunakan konsol pengelola paket dan voila, migrasi menggunakan RenameTable (...) secara otomatis dibuat untuk saya. Saya harus mengakui bahwa saya memastikan bahwa satu-satunya perubahan pada entitas adalah mengganti namanya sehingga tidak ada kolom baru atau kolom yang diganti namanya sehingga saya tidak dapat memastikan apakah ini adalah hal EF6 atau hanya bahwa EF (selalu) dapat mendeteksi migrasi sederhana seperti itu.

naskew
sumber
2
Saya dapat mengonfirmasi hal ini dengan 6.1.3 Ini benar mengubah nama tabel (jangan lupa untuk mengganti nama DbSetdi Anda DatabaseContextjuga). Mengubah kunci utama memang menyebabkan masalah. Migrasi akan mencoba menghapusnya dan membuat yang baru. Jadi Anda perlu menyesuaikan itu dan melakukan apa yang dijawab Chev, ganti nama kolomnya.
CularBytes
1

Nama tabel dan nama kolom dapat ditentukan sebagai bagian dari pemetaan DbContext. Maka tidak perlu melakukannya dalam migrasi.

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");
            });
     }
}
Martin Staufcik
sumber