web-dev-qa-db-fra.com

Code EF5 d'abord - Modification d'un type de colonne avec des migrations

Je suis nouveau sur EF5 Code First et je bricole avec une preuve de concept avant de me lancer dans un projet au travail.

J'ai d'abord créé un modèle qui ressemblait à quelque chose

public class Person {
  public int Id { get; set; }
  public string FirstName { get; set;}
  public string Surname {get;set;}
  public string Location {get;set;}
}

Et j'ai ajouté quelques disques en utilisant une petite application MVC que j'ai collée sur le dessus.

Maintenant, je veux changer la colonne Location en une énumération, quelque chose comme:

public class Person {
  public int Id { get; set; }
  public string FirstName { get; set;}
  public string Surname {get;set;}
  public Locations Location {get;set;}
}

public enum Locations {
  London = 1,
  Edinburgh = 2,
  Cardiff = 3
}

Lorsque j'ajoute la nouvelle migration, j'obtiens:

AlterColumn("dbo.People", "Location", c => c.Int(nullable: false));

mais lorsque j'exécute update-database, j'obtiens une erreur

Conversion failed when converting the nvarchar value 'London' to data type int.

Existe-t-il un moyen dans la migration de tronquer la table avant d'exécuter l'instruction alter?

Je sais que je peux ouvrir la base de données et le faire manuellement, mais existe-t-il un moyen plus intelligent?

37
Nick

La façon la plus intelligente est probablement de ne pas modifier les types. Si vous devez le faire, je vous suggère de suivre les étapes suivantes:

  1. Ajoutez une nouvelle colonne avec votre nouveau type
  2. Utilisez Sql() pour reprendre les données de la colonne d'origine à l'aide d'une instruction de mise à jour
  3. Supprimer l'ancienne colonne
  4. Renommer la nouvelle colonne

Tout cela peut être fait dans la même migration, le script SQL correct sera créé. Vous pouvez ignorer l'étape 2 si vous souhaitez que vos données soient supprimées. Si vous souhaitez le reprendre, ajoutez l'instruction appropriée (peut également contenir une instruction switch).

Malheureusement, Code First Migrations ne fournit pas de moyens plus simples pour y parvenir.

Voici l'exemple de code:

AddColumn("dbo.People", "LocationTmp", c => c.Int(nullable: false));
Sql(@"
    UPDATE dbp.People
    SET LocationTmp =
        CASE Location
            WHEN 'London' THEN 1
            WHEN 'Edinburgh' THEN 2
            WHEN 'Cardiff' THEN 3
            ELSE 0
        END
    ");
DropColumn("dbo.People", "Location");
RenameColumn("dbo.People", "LocationTmp", "Location");
53

Basé sur la réponse de @ JustAnotherUserYouMayKnow, mais plus facile.

Essayez tout d'abord d'exécuter la commande Sql() puis AlterColumn():

Sql(@"
    UPDATE dbo.People
    SET Location =
        CASE Location
            WHEN 'London' THEN 1
            WHEN 'Edinburgh' THEN 2
            WHEN 'Cardiff' THEN 3
            ELSE 0
        END
    ");
AlterColumn("dbo.People", "Location", c => c.Int(nullable: false));
17
Seven

Je sais que cela ne s'applique pas directement à la question mais pourrait être utile à quelqu'un. Dans mon problème, j'ai accidentellement fait un champ année un datetime et j'essayais de comprendre comment supprimer toutes les données, puis basculer le type de données vers un int.

Lors d'une migration d'ajout, EF voulait simplement mettre à jour la colonne. J'ai dû supprimer ce qu'ils voulaient faire et ajouter mon propre code. J'ai simplement laissé tomber la colonne et ajouté une nouvelle colonne. Voici ce qui a fonctionné pour moi.

protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.DropColumn(
            name: "TestingPeriodYear",
            table: "ControlActivityIssue");

        migrationBuilder.AddColumn<int>(
            name: "TestingPeriodYear",
            table: "ControlActivityIssue",
            nullable: true);
    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.DropColumn(
            name: "TestingPeriodYear",
            table: "ControlActivityIssue");

        migrationBuilder.AddColumn<DateTime>(
            name: "TestingPeriodYear",
            table: "ControlActivityIssue",
            nullable: true);
    }
1
Ben