2016-03-15 7 views
7

Ich benutze EF Code-First-Ansatz und will das Id Feld zu guid ändern, kann aber nicht scheinen, um unter Fehler zu kommen.Wie kann ich eine int ID-Spalte mit EF Migration in Guid ändern?

Dies ist meine erste Migration:

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

Später erkannte ich, dass ich meine Id Spalte müssen GUID werden, damit ich meine Modelldatei geändert:

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

Wenn Add-Migration ChangeDownloadTokenIdToGuid läuft erzeugt es diese Datei :

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

Ausführen dieser Datei mit Update-Database verursacht diesen Fehler:

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. 

Irgendwelche Ideen, warum dies passieren könnte?

Antwort

13

Es verursacht wurde, weil es unmöglich ist, vorherige int Art von Id Spalte Guid Typ zu konvertieren (genau das versuchen AlterColumn Verfahren durchzuführen). Außerdem schlägt die Fehlermeldung vor, dass der neue Typ der Id Spalte eine der folgenden sein kann: int, bigint, smallint, tinyint, oder dezimal oder numerisch mit einer Skala von 0, für sie ist es möglich, eine Konvertierung durchzuführen von int Typ.

Lösung - einfach Id Säule fallen lassen und es dann mit neuem Guid Typ neu erstellen, Migration auf diese Weise ändern:

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

Auch Slava Utesinov Arbeiten aber es funktioniert nur auf leeren Tischen oder in Fälle, in denen keine anderen Tabellen auf die Tabelle verweisen, die Sie konvertieren. Diese Antwort hilft also denjenigen, die auf dieser Seite landen, mit einer komplexeren Datenbankeinrichtung.

Im Folgenden finden Sie eine Dienstprogrammfunktion, die Sie aus Ihrer Migrationsklasse verwenden können. Diese Funktion sollte über die Up/Down-Funktionen aufgerufen werden. Die Funktion behandelt auch Tabellen, die auf die Tabelle verweisen, die Sie von Int in Guid konvertieren möchten. Diese Hilfsfunktion geht davon aus, dass die zu konvertierende Spalte "Id" heißt, sollte aber ansonsten eher generisch sein.

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

Also in Ihrem Fall Ihre Up/Down-Funktionen seien:

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

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