Migrations Entity Framework renommer des tables et des colonnes

118

J'ai renommé quelques entités et leurs propriétés de navigation et généré une nouvelle migration dans EF 5. Comme d'habitude avec les renommages dans les migrations EF, par défaut, il allait supprimer des objets et les recréer. Ce n'est pas ce que je voulais, alors j'ai à peu près dû créer le fichier de migration à partir de zéro.

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

Tout ce que je suis en train de faire est de renommage dbo.ReportSectionsà dbo.ReportPagespuis dbo.ReportSectionGroupsà dbo.ReportSections. Ensuite, je dois renommer la colonne de clé étrangère dbo.ReportPagesde Group_Idà Section_Id.

Je laisse tomber les clés étrangères et les index reliant les tables ensemble, puis je renomme les tables et la colonne de clé étrangère, puis j'ajoute à nouveau les index et les clés étrangères. J'ai supposé que cela allait fonctionner mais j'obtiens une erreur SQL.

Msg 15248, niveau 11, état 1, procédure sp_rename, ligne 215 Soit le paramètre @objname est ambigu, soit le @objtype (COLUMN) déclaré est erroné. Msg 4902, niveau 16, état 1, ligne 10 Impossible de trouver l'objet «dbo.ReportSections» car il n'existe pas ou vous ne disposez pas des autorisations.

Je n'ai pas le temps de comprendre ce qui ne va pas ici. Toute idée serait extrêmement utile.

Chev
la source
Laquelle des lignes ci-dessus échoue? Pouvez-vous suivre la migration dans SQL Server Profiler et vérifier le SQL correspondant?
Albin Sunnanbo

Réponses:

143

Ça ne fait rien. Je rendais ce chemin plus compliqué qu'il ne le fallait vraiment.

C'était tout ce dont j'avais besoin. Les méthodes de changement de nom génèrent simplement un appel à la procédure stockée système sp_rename et je suppose que cela s'est occupé de tout, y compris les clés étrangères avec le nouveau nom de colonne.

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
la source
29
Soyez prudent avec les noms de table qui contiennent des points. RenameColumngénère une sp_renameinstruction T-SQL qui utilise des utilisations en parsenameinterne, ce qui présente certaines limitations. Donc, si vous avez un nom de table contenant des points, par exemple "SubSystemA.Tablename", utilisez:RenameColumn("dbo.[SubSystemA.Tablename]", "OldColumnName", "NewColumnName");
Ilan
10
Cela semble mettre à jour les colonnes référencées dans les clés étrangères, mais cela ne renomme pas le FK lui-même. Ce qui est dommage, mais probablement pas la fin du monde à moins que vous n'ayez absolument besoin de faire référence à un FK plus tard par son nom.
mikesigs
9
@mikesigs que vous pouvez utiliser RenameIndex(..)dans votre migration pour le renommer
JoeBrockhaus
1
Je reçois une exception lors du changement de nom de la colonne. probablement parce que la table de changement de nom n'est toujours pas appliquée. J'ai dû le diviser en deux migrations
Josue Martinez
Avec EF6, utilisez RenameTable(..)pour renommer les FK et PK. Cela ne semble pas correct, mais c'est ce qui a fonctionné pour moi. C'est la méthode qui crée le bon T-SQL ( execute sp_rename ...). Si vous faites update-database -verbose, vous le verrez par vous-même.
Giovanni le
44

Si vous n'aimez pas écrire / modifier manuellement le code requis dans la classe Migration, vous pouvez suivre une approche en deux étapes qui crée automatiquement le RenameColumncode requis:

Étape 1 Utilisez le ColumnAttributepour introduire le nouveau nom de colonne, puis ajoutez-migration (par exemple Add-Migration ColumnChanged)

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

Étape 2: modifiez le nom de la propriété et appliquez à nouveau la même migration (par exemple Add-Migration ColumnChanged -force) dans la console du gestionnaire de package

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

Si vous regardez la classe Migration, vous pouvez voir le code généré automatiquement RenameColumn.

Hossein Narimani Rad
la source
Comment pouvez-vous ajouter deux fois la même migration? Quand j'essaye ceci, j'obtiens:The name 'Rename_SalesArea' is used by an existing migration.
Andrew S
jetez un œil au -forceparamètre lors de l'utilisation d'add-migration
Hossein Narimani Rad
2
faites également attention ce message n'est pas pour EF core
Hossein Narimani Rad
6
Je pense que vous n'avez besoin que d'une migration, mais toujours de deux étapes. 1. Ajoutez un attribut et créez "renommer la migration". 2. Changez simplement le nom de la propriété. C'est tout. De toute façon, cela m'a fait gagner beaucoup de temps. Merci!
Crispy Ninja
1
J'ai suivi les étapes mentionnées ici et cela a réussi. Je n'ai perdu aucune donnée existante. c'est ce que je voulais vraiment, faire les changements sans perdre les données. Mais j'exécute la migration différente après avoir renommé le nom de propriété de la classe, pour des raisons de sécurité.
Manojb86
19

Pour développer un peu la réponse de Hossein Narimani Rad, vous pouvez renommer à la fois une table et des colonnes en utilisant respectivement System.ComponentModel.DataAnnotations.Schema.TableAttribute et System.ComponentModel.DataAnnotations.Schema.ColumnAttribute.

Cela présente quelques avantages:

  1. Non seulement cela créera automatiquement les migrations de noms, mais
  2. il supprimera également délicieusement toutes les clés étrangères et les recréera avec les nouveaux noms de table et de colonne, en donnant les noms propres aux clés étrangères et aux contraintes.
  3. Tout cela sans perdre aucune donnée de table

Par exemple, ajouter [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; }
}

Générera la 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
la source
1
On dirait que cela devrait être la valeur par défaut pour ajouter l'attribut de table, cela rend les choses tellement plus simples.
patrick
17

Dans EF Core, j'utilise les instructions suivantes pour renommer les tables et les colonnes:

Quant au changement de nom des tables:

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

Quant à renommer les colonnes:

    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
la source
3

Dans ef core, vous pouvez modifier la migration créée après l'ajout de la migration. Et puis faites update-database. Un échantillon a donné ci-dessous:

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
la source
2

J'ai juste essayé la même chose dans EF6 (renommer la première entité du code). J'ai simplement renommé la classe et ajouté une migration à l'aide de la console du gestionnaire de packages et voilà, une migration à l'aide de RenameTable (...) a été automatiquement générée pour moi. Je dois admettre que je me suis assuré que le seul changement apporté à l'entité était de le renommer, donc pas de nouvelles colonnes ou de colonnes renommées, donc je ne peux pas être certain s'il s'agit d'une chose EF6 ou simplement qu'EF était (toujours) capable de détecter ces migrations simples.

naskew
la source
2
Je peux le confirmer avec 6.1.3 Il renomme correctement la table (n'oubliez pas de renommer le DbSetdans votre DatabaseContextégalement). La modification de la clé primaire pose des problèmes. La migration essaiera de le supprimer et d'en créer un nouveau. Vous devez donc ajuster cela et faire comme la réponse de Chev, renommer la colonne.
CularBytes
1

Les noms de table et les noms de colonne peuvent être spécifiés dans le cadre du mappage de DbContext. Ensuite, il n'est pas nécessaire de le faire dans les migrations.

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
la source