2016-03-15 23 views
7

Korzystam z podejścia opartego na kodowaniu EF i chcę zmienić pole Id na guid, ale nie mogę pominąć błędu.Jak zmienić kolumnę identyfikatora int na Guid z migracją EF?

To moja pierwsza migracja:

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

Później zdałem sobie sprawę, że muszę mój Id kolumna być GUID więc zmieniłem plik modelu

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

Uruchamiając Add-Migration ChangeDownloadTokenIdToGuid to generuje ten plik :

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

Uruchomienie pliku z Update-Database powoduje ten błąd:

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. 

Jakieś pomysły, dlaczego tak się dzieje?

Odpowiedz

13

Spowodowane to było dlatego, że jest niemożliwe, aby przekształcić poprzednie int rodzaj Id kolumna Guid typu (dokładnie, które próbują wykonać AlterColumn metody). Ponadto, komunikat o błędzie sugeruje, że nowy typ kolumny Id może być jednym z typów z zestawu: int, bigint, smallint, tinyint, lub dziesiętny lub numeryczny ze skalą 0, dla nich możliwe jest przeprowadzenie konwersji od typu int.

Rozwiązanie - wystarczy kropla Id kolumnę, a następnie odtworzyć go nowym Guid typu, zmienić migrację sposób:

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

Nawet ThoughtWorks Slava Utesinov jest, to działa tylko na pustych stołach lub w przypadki, w których żadne inne tabele nie odnoszą się do tabeli, którą konwertujesz. Ta odpowiedź pomoże tym, którzy kończą na tej stronie, w bardziej złożonej konfiguracji bazy danych.

Poniżej znajduje się funkcja narzędzia, której można użyć z poziomu klasy migracji, która powinna być wywoływana z poziomu funkcji Up/Down. Funkcja obsługuje również tabele odwołujące się do tabeli, którą próbujesz przekonwertować z Int do Guid. Ta funkcja pomocnika zakłada, że ​​kolumna, którą konwertujesz, nazywa się "Id", ale w przeciwnym razie powinna być raczej ogólna.

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

Więc w przypadku, gdy w górę/dół funkcje byłoby:

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

    public override void Down() 
    { 
     Convert(false, "DownloadToken"); 
    } 
Powiązane problemy