Entity Framework-Migrationen, die Tabellen und Spalten umbenennen

118

Ich habe einige Entitäten und ihre Navigationseigenschaften umbenannt und eine neue Migration in EF 5 generiert. Wie bei Umbenennungen in EF-Migrationen üblich, wurden Objekte standardmäßig gelöscht und neu erstellt. Das wollte ich nicht, also musste ich die Migrationsdatei so ziemlich von Grund auf neu erstellen.

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

Ich versuche nur, dbo.ReportSectionsin dbo.ReportPagesund dann dbo.ReportSectionGroupsin umzubenennen dbo.ReportSections. Dann muss ich die Fremdschlüsselspalte von dbo.ReportPagesvon Group_Idnach umbenennen Section_Id.

Ich lösche die Fremdschlüssel und Indizes, die die Tabellen miteinander verbinden, benenne die Tabellen und die Fremdschlüsselspalte um und füge dann die Indizes und Fremdschlüssel erneut hinzu. Ich nahm an, dass dies funktionieren würde, aber ich erhalte einen SQL-Fehler.

Nachricht 15248, Ebene 11, Status 1, Prozedur sp_rename, Zeile 215 Entweder ist der Parameter @objname mehrdeutig oder der beanspruchte @objtype (COLUMN) ist falsch. Nachricht 4902, Ebene 16, Status 1, Zeile 10 Das Objekt "dbo.ReportSections" kann nicht gefunden werden, da es nicht vorhanden ist oder Sie keine Berechtigungen haben.

Es fällt mir nicht leicht herauszufinden, was hier falsch ist. Jeder Einblick wäre enorm hilfreich.

Chev
quelle
Welche der obigen Zeilen schlägt fehl? Können Sie die Migration in SQL Server Profiler verfolgen und das entsprechende SQL überprüfen?
Albin Sunnanbo

Antworten:

143

Keine Ursache. Ich habe diesen Weg komplizierter gemacht, als es wirklich sein musste.

Das war alles was ich brauchte. Die Umbenennungsmethoden generieren nur einen Aufruf der gespeicherten Prozedur des Systems sp_rename , und ich denke, das hat sich um alles gekümmert, einschließlich der Fremdschlüssel mit dem neuen Spaltennamen.

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
quelle
29
Seien Sie vorsichtig mit Tabellennamen, die Punkte enthalten. RenameColumngeneriert eine sp_renameT-SQL-Anweisung, die parsenameinterne Verwendungen verwendet, die einige Einschränkungen aufweisen. Wenn Sie also einen Tabellennamen haben, der Punkte enthält, z. B. "SubSystemA.Tabellenname", verwenden Sie:RenameColumn("dbo.[SubSystemA.Tablename]", "OldColumnName", "NewColumnName");
Ilan
10
Dies scheint die Spalten zu aktualisieren, auf die in den Fremdschlüsseln verwiesen wird, benennt jedoch die FK selbst nicht um. Das ist eine Schande, aber wahrscheinlich nicht das Ende der Welt, es sei denn, Sie müssen sich später unbedingt auf einen FK mit seinem Namen beziehen.
Mikeigsigs
9
@mikesigs können Sie RenameIndex(..)in Ihrer Migration verwenden, um es umzubenennen
JoeBrockhaus
1
Beim Umbenennen der Spalte wird eine Ausnahme angezeigt. wahrscheinlich, weil die Umbenennungstabelle immer noch nicht angewendet wird. Ich musste es in zwei Migrationen aufteilen
Josue Martinez
Verwenden Sie mit EF6, um die FKs und PKs RenameTable(..)umzubenennen. Klingt nicht richtig, aber es hat bei mir funktioniert. Es ist die Methode, die das richtige T-SQL ( execute sp_rename ...) erstellt. Wenn Sie update-database -verbose ausführen, werden Sie es selbst sehen.
Giovanni
44

Wenn Sie den erforderlichen Code in der Migrationsklasse nicht manuell schreiben / ändern möchten, können Sie einen zweistufigen Ansatz verfolgen, bei dem der erforderliche Code automatisch erstellt RenameColumnwird:

Erster Schritt Verwenden Sie die ColumnAttributeden neuen Spaltennamen einzuführen und dann Add-Migration (zB Add-Migration ColumnChanged)

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

Schritt 2 Ändern Sie den Eigenschaftsnamen und wenden Sie ihn erneut auf dieselbe Migration (z. B. Add-Migration ColumnChanged -force) in der Package Manager-Konsole an

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

Wenn Sie sich die Migrationsklasse ansehen, sehen Sie, dass der automatisch generierte Code lautet RenameColumn.

Hossein Narimani Rad
quelle
Wie können Sie dieselbe Migration zweimal hinzufügen? Wenn ich das versuche, bekomme ich:The name 'Rename_SalesArea' is used by an existing migration.
Andrew S
Werfen Sie einen Blick auf -forceParameter bei der Verwendung von Add-Migration
Hossein Narimani Rad
2
Achten Sie
6
Ich denke, Sie brauchen nur eine Migration, aber immer noch zwei Schritte. 1. Fügen Sie ein Attribut hinzu und erstellen Sie "Migration umbenennen". 2. Ändern Sie einfach den Eigenschaftsnamen. Das ist es. Auf jeden Fall hat mir das eine Menge Zeit gespart. Vielen Dank!
Knusprige Ninja
1
Ich habe die hier genannten Schritte befolgt und es war erfolgreich. Ich habe keine vorhandenen Daten verloren. Das, was ich wirklich wollte, nehmen Sie die Änderungen vor, ohne die Daten zu verlieren. Aus Sicherheitsgründen führe ich die andere Migration aus, nachdem ich den Eigenschaftsnamen der Klasse umbenannt habe.
Manojb86
19

Um die Antwort von Hossein Narimani Rad ein wenig zu erweitern, können Sie eine Tabelle und Spalten mit System.ComponentModel.DataAnnotations.Schema.TableAttribute bzw. System.ComponentModel.DataAnnotations.Schema.ColumnAttribute umbenennen.

Dies hat einige Vorteile:

  1. Dadurch werden nicht nur die Namensmigrationen automatisch erstellt, sondern auch
  2. Außerdem werden Fremdschlüssel auf köstliche Weise gelöscht und anhand der neuen Tabellen- und Spaltennamen neu erstellt, wobei den Fremdschlüsseln und Konstanten Eigennamen zugewiesen werden.
  3. All dies, ohne Tabellendaten zu verlieren

Zum Beispiel hinzufügen [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; }
}

Erzeugt die Migration:

    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
quelle
1
Es scheint, als ob es die Standardeinstellung sein sollte, das Tabellenattribut hinzuzufügen, was die Dinge so viel einfacher macht.
Patrick
17

In EF Core verwende ich die folgenden Anweisungen, um Tabellen und Spalten umzubenennen:

Zum Umbenennen von Tabellen:

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

Zum Umbenennen von Spalten:

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

In ef core können Sie die Migration ändern, die nach dem Hinzufügen der Migration erstellt wurde. Und dann Update-Datenbank. Ein Beispiel hat unten gegeben:

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
quelle
2

Ich habe es gerade in EF6 versucht (Code First Entity Rename). Ich habe die Klasse einfach umbenannt und eine Migration über die Paketmanagerkonsole und voila hinzugefügt. Eine Migration mit RenameTable (...) wurde automatisch für mich generiert. Ich muss zugeben, dass ich sichergestellt habe, dass die einzige Änderung an der Entität darin bestand, sie umzubenennen, sodass keine neuen Spalten oder umbenannten Spalten vorhanden sind. Daher kann ich nicht sicher sein, ob dies eine EF6-Sache ist oder ob EF (immer) in der Lage war, solche einfachen Migrationen zu erkennen.

naskew
quelle
2
Ich kann dies mit 6.1.3 bestätigen. Die Tabelle wird korrekt umbenannt (vergessen Sie nicht, die Tabelle auch DbSetin Ihrer umzubenennen DatabaseContext). Das Ändern des Primärschlüssels verursacht Probleme. Bei der Migration wird versucht, sie zu löschen und eine neue zu erstellen. Sie müssen dies also anpassen und die Spalte umbenennen, wie es Chevs Antwort lautet.
CularBytes
1

Tabellennamen und Spaltennamen können im Rahmen der Zuordnung von angegeben werden DbContext. Dann ist dies bei Migrationen nicht erforderlich.

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
quelle