Entity Framework Migrations ridenominazione di tabelle e colonne


118

Ho rinominato un paio di entità e le loro proprietà di navigazione e ho generato una nuova migrazione in EF 5. Come al solito con le ridenominazioni nelle migrazioni EF, per impostazione predefinita stava per rilasciare oggetti e ricrearli. Non è quello che volevo, quindi ho praticamente dovuto creare il file di migrazione da zero.

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

Tutto quello che sto cercando di fare è rinominare dbo.ReportSectionsin dbo.ReportPagese poi dbo.ReportSectionGroupsin dbo.ReportSections. Quindi ho bisogno di rinominare la colonna della chiave esterna dbo.ReportPagesda Group_Ida Section_Id.

Sto rilasciando le chiavi esterne e gli indici che collegano le tabelle insieme, quindi sto rinominando le tabelle e la colonna della chiave esterna, quindi aggiungo di nuovo gli indici e le chiavi esterne. Pensavo che avrebbe funzionato ma ricevo un errore SQL.

Msg 15248, livello 11, stato 1, procedura sp_rename, riga 215 Il parametro @objname è ambiguo oppure @objtype (COLUMN) dichiarato è errato. Msg 4902, livello 16, stato 1, riga 10 Impossibile trovare l'oggetto "dbo.ReportSections" perché non esiste o non si dispone delle autorizzazioni.

Non riesco a capire cosa c'è che non va qui. Qualsiasi intuizione sarebbe estremamente utile.


Quale delle righe precedenti non funziona? Puoi tracciare la migrazione in SQL Server Profiler e controllare l'SQL corrispondente?
— Albin Sunnanbo

Risposte:


143

Non importa. Stavo rendendo questo modo più complicato di quanto avrebbe dovuto essere.

Questo era tutto ciò di cui avevo bisogno. I metodi rename generano semplicemente una chiamata alla stored procedure di sistema sp_rename e immagino che si sia preso cura di tutto, comprese le chiavi esterne con il nuovo nome di colonna.

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
Fai attenzione ai nomi delle tabelle che contengono dei punti. RenameColumngenera sp_renameun'istruzione T-SQL che utilizza usi parsenameinternamente che presenta alcune limitazioni. Quindi, se hai un nome di tabella che contiene dei punti, ad esempio "SubSystemA.Tablename", utilizza:RenameColumn("dbo.[SubSystemA.Tablename]", "OldColumnName", "NewColumnName");
— Ilan

10
Ciò sembra aggiornare le colonne a cui si fa riferimento nelle chiavi esterne, ma non rinomina l'FK stesso. Il che è un peccato, ma probabilmente non è la fine del mondo a meno che non sia assolutamente necessario fare riferimento a un FK in seguito con il suo nome.
— mikesigs

9
@mikesigs che puoi utilizzare RenameIndex(..)nella migrazione per rinominarlo
— JoeBrockhaus,

1
Ricevo un'eccezione durante la ridenominazione della colonna. probabilmente perché la tabella di ridenominazione non è ancora applicata. Ho dovuto dividerlo in due migrazioni
— Josue Martinez il

Con EF6, utilizzare RenameTable(..)per rinominare FK e PK. Non suona bene ma è quello che ha funzionato per me. È il metodo che crea il corretto T-SQL ( execute sp_rename ...). Se esegui update-database -verbose, lo vedrai da solo.
— Giovanni

44

Se non ti piace scrivere / modificare manualmente il codice richiesto nella classe Migration, puoi seguire un approccio in due fasi che crea automaticamente il RenameColumncodice richiesto:

Passaggio uno Utilizzare ColumnAttributeper introdurre il nuovo nome della colonna e quindi aggiungere la migrazione (ad es. Add-Migration ColumnChanged)

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

Passaggio due: modificare il nome della proprietà e applicare nuovamente alla stessa migrazione (ad esempio Add-Migration ColumnChanged -force) nella console di Gestione pacchetti

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

Se guardi la classe Migration puoi vedere che il codice generato automaticamente è RenameColumn.


Come puoi aggiungere due volte la stessa migrazione? Quando provo questo, ottengo:The name 'Rename_SalesArea' is used by an existing migration.
— Andrew S

dai un'occhiata al -forceparametro quando si utilizza la migrazione aggiuntiva
— Hossein Narimani Rad

2
anche prestare attenzione questo post non è per EF core
— Hossein Narimani Rad

6
Penso che sia necessaria solo una migrazione, ma comunque due passaggi. 1. Aggiungere l'attributo e creare "Rinomina migrazione" 2. Basta cambiare il nome della proprietà. Questo è tutto. Ad ogni modo, questo mi ha fatto risparmiare un sacco di tempo. Grazie!
— Crispy Ninja

1
Ho seguito i passaggi menzionati qui e ha avuto successo. Non ho perso alcun dato esistente. quello che volevo veramente, apportare le modifiche senza perdere i dati. Ma eseguo la migrazione diversa dopo aver rinominato il nome della proprietà della classe, per sicurezza.
— Manojb86

19

Per espandere un po 'la risposta di Hossein Narimani Rad, puoi rinominare sia una tabella che delle colonne usando rispettivamente System.ComponentModel.DataAnnotations.Schema.TableAttribute e System.ComponentModel.DataAnnotations.Schema.ColumnAttribute.

Questo ha un paio di vantaggi:

  1. Non solo questo creerà automaticamente le migrazioni del nome, ma
  2. eliminerà anche deliziosamente tutte le chiavi esterne e le ricrea contro i nuovi nomi di tabella e colonna, dando alle chiavi esterne e ai nomi propri dei vincoli.
  3. Tutto questo senza perdere alcun dato della tabella

Ad esempio, aggiungendo [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; }
}

Genererà la migrazione:

    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
Sembra che dovrebbe essere l'impostazione predefinita per aggiungere l'attributo table, rende le cose molto più semplici.
— patrick

17

In EF Core, utilizzo le seguenti istruzioni per rinominare tabelle e colonne:

Per quanto riguarda la ridenominazione delle tabelle:

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

Per quanto riguarda la ridenominazione delle colonne:

    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

In ef core, puoi modificare la migrazione creata dopo l'aggiunta della migrazione. E poi aggiorna-database. Un esempio ha fornito di seguito:

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

Ho appena provato lo stesso in EF6 (rinomina entità codice prima). Ho semplicemente rinominato la classe e aggiunto una migrazione utilizzando la console del gestore pacchetti e voilà, una migrazione utilizzando RenameTable (...) è stata generata automaticamente per me. Devo ammettere che mi sono assicurato che l'unica modifica all'entità fosse rinominarla in modo che non ci siano nuove colonne o colonne rinominate, quindi non posso essere certo se si tratta di una cosa EF6 o semplicemente che EF fosse (sempre) in grado di rilevare migrazioni così semplici.


2
Posso confermarlo con 6.1.3 Rinomina correttamente la tabella (non dimenticare di rinominare anche il DbSetnel tuo DatabaseContext). La modifica della chiave primaria causa problemi. La migrazione proverà a eliminarlo e a crearne uno nuovo. Quindi devi aggiustarlo e fare come la risposta di Chev è, rinominare la colonna.
— CularBytes

1

I nomi delle tabelle e delle colonne possono essere specificati come parte della mappatura di DbContext. Quindi non è necessario farlo nelle migrazioni.

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");
            });
     }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.