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.