2016-03-15 11 views
7

Sto utilizzando un approccio con codice EF e voglio modificare il campo Id su guid ma non riesco a passare oltre l'errore.Come posso modificare una colonna ID int a Guid con migrazione EF?

Questa è la mia prima migrazione:

public partial class CreateDownloadToken : DbMigration 
{ 
    public override void Up() 
    { 
     CreateTable(
      "dbo.DownloadTokens", 
      c => new 
      { 
       Id = c.Int(nullable: false, identity: true), 
       FileId = c.Int(), 
       UserId = c.String(nullable: false, maxLength: 128), 
       ValidUntil = c.DateTime(nullable: false), 
      }) 
      .PrimaryKey(t => t.Id) 
      .ForeignKey("dbo.Files", t => t.FileId) 
      .ForeignKey("dbo.Users", t => t.UserId, cascadeDelete: true) 
      .Index(t => t.FileId) 
      .Index(t => t.UserId); 

    } 

    public override void Down() 
    { 
     DropForeignKey("dbo.DownloadTokens", "UserId", "dbo.Users"); 
     DropForeignKey("dbo.DownloadTokens", "FileId", "dbo.Files"); 
     DropIndex("dbo.DownloadTokens", new[] { "UserId" }); 
     DropIndex("dbo.DownloadTokens", new[] { "FileId" }); 
     DropTable("dbo.DownloadTokens"); 
    } 
} 

Più tardi mi sono reso conto che ho bisogno di mia colonna Id da GUID così ho cambiato il mio file di modello:

public class DownloadToken 
{ 
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Computed)] 
    public Guid Id { get; set; } 

    public int? FileId { get; set; } 

    [ForeignKey("FileId")] 
    public virtual File File { get; set; } 

    [Required] 
    public string UserId { get; set; } 

    [ForeignKey("UserId")] 
    public virtual User User { get; set; } 

    [Required] 
    public DateTime ValidUntil { get; set; } 
} 

Quando si esegue Add-Migration ChangeDownloadTokenIdToGuid genera questo file :

public partial class ChangeDownloadTokenIdToGuid : DbMigration 
{ 
    public override void Up() 
    { 
     DropPrimaryKey("dbo.DownloadTokens"); 
     AlterColumn("dbo.DownloadTokens", "Id", c => c.Guid(nullable: false)); 
     AddPrimaryKey("dbo.DownloadTokens", "Id"); 
    } 

    public override void Down() 
    { 
     DropPrimaryKey("dbo.DownloadTokens"); 
     AlterColumn("dbo.DownloadTokens", "Id", c => c.Int(nullable: false, identity: true)); 
     AddPrimaryKey("dbo.DownloadTokens", "Id"); 
    } 
} 

Esecuzione di questo file con Update-Database causa questo errore:

Identity column 'Id' must be of data type int, bigint, smallint, tinyint, or decimal or numeric with a scale of 0, and constrained to be nonnullable. 

Qualche idea perché questo potrebbe accadere?

risposta

13

È stato causato perché è impossibile convertire precedente int tipo di Id colonna Guid tipo (esattamente che tenta di eseguire AlterColumn metodo). Inoltre, il messaggio di errore suggerisce che il nuovo tipo di colonna Id può essere di tipo dal set: int, bigint, smallint, tinyint o decimale o numerico con una scala di 0, per loro è possibile eseguire la conversione dal tipo int.

Soluzione - semplicemente cadere Id colonna e quindi ricrearlo con la nuova Guid tipo, cambiare la migrazione in questo modo:

public partial class ChangeDownloadTokenIdToGuid : DbMigration 
{ 
    public override void Up() 
    { 
     DropPrimaryKey("dbo.DownloadTokens"); 

     DropColumn("dbo.DownloadTokens", "Id"); 
     AddColumn("dbo.DownloadTokens", "Id", c => c.Guid(nullable: false, identity: true)); 

     AddPrimaryKey("dbo.DownloadTokens", "Id"); 
    } 

    public override void Down() 
    { 
     DropPrimaryKey("dbo.DownloadTokens"); 

     DropColumn("dbo.DownloadTokens", "Id"); 
     AddColumn("dbo.DownloadTokens", "Id", c => c.Int(nullable: false, identity: true)); 

     AddPrimaryKey("dbo.DownloadTokens", "Id"); 
    } 
} 

P.S. Why you use DatabaseGeneratedOption.Computed attribute, not DatabaseGeneratedOption.Identity ?

7

Anche se le opere di Slava Utesinov, funziona solo su tavoli vuoti o in casi in cui nessun'altra tabella si riferisce alla tabella che stai convertendo. Quindi questa risposta aiuterà coloro che finiscono in questa pagina con una configurazione di database più complessa.

Di seguito è riportata una funzione di utilità che è possibile utilizzare dalla classe di migrazione, che deve essere richiamata dalle funzioni Su/Giù. La funzione gestisce anche i riferimenti alle tabelle della tabella che stai cercando di convertire da Int a Guid. Questa funzione di supporto presuppone che la colonna che stai convertendo si chiami 'Id', ma in caso contrario dovrebbe essere piuttosto generica.

public void Convert(bool toGuid, string parent, params string[] children) 
    { 
     if (toGuid) 
     { 
      AddColumn($"dbo.{parent}s", "Id2", c => c.Guid(nullable: false, identity: true, defaultValueSql: "newid()")); 
     } 
     else 
     { 
      AddColumn($"dbo.{parent}s", "Id2", c => c.Int(nullable: false, identity: true)); 
     } 
     foreach (var child in children) 
     { 
      DropForeignKey($"dbo.{child}s", $"{parent}_Id", $"dbo.{parent}s"); 
      DropIndex($"dbo.{child}s", new[] { $"{parent}_Id" }); 
      RenameColumn($"dbo.{child}s", $"{parent}_Id", $"old_{parent}_Id"); 
      if (toGuid) 
      { 
       AddColumn($"dbo.{child}s", $"{parent}_Id", c => c.Guid()); 
      } 
      else 
      { 
       AddColumn($"dbo.{child}s", $"{parent}_Id", c => c.Int()); 
      } 
      Sql($"update c set {parent}_Id=p.Id2 from {child}s c inner join {parent}s p on p.Id=c.old_{parent}_Id"); 
      DropColumn($"dbo.{child}s", $"old_{parent}_Id"); 
     } 
     DropPrimaryKey($"dbo.{parent}s"); 
     DropColumn($"dbo.{parent}s", "Id"); 
     RenameColumn($"dbo.{parent}s", "Id2", "Id"); 
     AddPrimaryKey($"dbo.{parent}s", "Id"); 
     foreach (var child in children) 
     { 
      CreateIndex($"dbo.{child}s", $"{parent}_Id"); 
      AddForeignKey($"dbo.{child}s", $"{parent}_Id", $"dbo.{parent}s", "Id"); 
     } 
    } 

Quindi nel tuo caso i vostri le funzioni/Giù sarebbero:

public override void Up() 
    { 
     Convert(true,"DownloadToken"); 
    } 

    public override void Down() 
    { 
     Convert(false, "DownloadToken"); 
    }