如何通过EF迁移将int ID列更改为Guid?

我正在使用EF代码优先方法,并希望将Id字段更改为guid但似乎无法通过以下错误。

这是我的第一次迁移:

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

后来我意识到我需要我的Id列是GUID所以我改变了我的模型文件:

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

运行Add-Migration ChangeDownloadTokenIdToGuid它会生成以下文件:

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

使用Update-Database运行此文件会导致此错误:

 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. 

任何想法为什么会这样?

这是因为无法将之前的int类型的Id列转换为Guid类型(正是尝试执行AlterColumn方法)。 此外,错误消息建议您,新类型的Id列可以是set中的类型之一: int,bigint,smallint,tinyint,或者小数或数字,其比例为0 ,对于它们,可以从int执行转换类型。

解决方案 – 只需删除Id列,然后使用新的Guid类型重新创建它,以这种方式更改迁移:

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

PS为什么使用DatabaseGeneratedOption.Computed属性,而不是DatabaseGeneratedOption.Identity

即使Slava Utesinov的作品,它只适用于空表或没有其他表指的是您正在转换的表。 因此,这个答案将帮助那些以更复杂的数据库设置结束此页面的人。

下面是一个可以在迁移类中使用的实用程序函数,应该可以从Up / Down函数调用它。 该函数还处理表引用您尝试从Int转换为Guid的表。 此辅助函数假定您要转换的列称为“Id”,但应该是相当通用的。

 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}sc inner join {parent}sp 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"); } } 

因此,在您的情况下,您的向上/向下function将是:

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