Code First从int迁移到Guid主键问题

时间:2015-08-10 18:24:36

标签: entity-framework-6

我正在尝试将我的代码第一个ID列从“int”更改为“Guid”,并且在尝试运行迁移时,我收到消息:

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

我正在定义这样的列:

public partial class CustomField : BaseEntity
{

    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public Guid CustomFieldId { get; set; }

在CustomFieldMapping.cs中映射它,如下所示:

public CustomFieldMapping()
{
    //Primary key
    HasKey(t => t.CustomFieldId);

    //Constraints 
    Property(t => t.CustomFieldId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

并且生成的迁移正在尝试执行此操作:

public override void Up()
{
    DropForeignKey("dbo.CustomField", "CustomFormId", "dbo.CustomForm");
    DropForeignKey("dbo.CustomData", "CustomFieldId", "dbo.CustomField");
    DropForeignKey("dbo.CustomForm", "ParentFormId", "dbo.CustomForm");
    DropIndex("dbo.CustomField", new[] { "CustomFormId" });
    DropIndex("dbo.CustomForm", new[] { "ParentFormId" });
    DropIndex("dbo.CustomData", new[] { "CustomFieldId" });
    DropPrimaryKey("dbo.CustomField");
    DropPrimaryKey("dbo.CustomForm");
    AlterColumn("dbo.CustomField", "CustomFieldId", c => c.Guid(nullable: false));
    AlterColumn("dbo.CustomField", "SortOrder", c => c.Int(nullable: false));
    AlterColumn("dbo.CustomForm", "CustomFormId", c => c.Guid(nullable: false));
    AlterColumn("dbo.CustomForm", "ParentFormId", c => c.Guid());
    AddPrimaryKey("dbo.CustomField", "CustomFieldId");
    AddPrimaryKey("dbo.CustomForm", "CustomFormId");
    CreateIndex("dbo.CustomField", "CustomForm_CustomFormId");
    CreateIndex("dbo.CustomForm", "ParentFormId");
    CreateIndex("dbo.CustomData", "CustomField_CustomFieldId");
    AddForeignKey("dbo.CustomField", "CustomForm_CustomFormId", "dbo.CustomForm", "CustomFormId");
    AddForeignKey("dbo.CustomData", "CustomField_CustomFieldId", "dbo.CustomField", "CustomFieldId");
    AddForeignKey("dbo.CustomForm", "ParentFormId", "dbo.CustomForm", "CustomFormId");

我希望它是一个顺序递增的Guid。我做错了什么?

2 个答案:

答案 0 :(得分:7)

要解决此问题,我在迁移类的Sql()Up()方法中使用了Down()方法。 Up()方法中的SQL命令字符串删除ID列上的主键约束,删除类型为int的ID列,然后添加类型为Guid的新ID列。 Down()方法执行相同操作,但会删除Guid列并添加新的int列。

我在Stack Overflow上找到了一些解决方案,它通过在查询窗口中运行SQL命令来解析“更改列类型”。为了解决你的评论:

  

我们只是想保持一个干净/清晰的迁移路径来追踪   我们用SQL做了一些并不容易的事情。

我在Up()Down()迁移方法中使用了SQL命令。对我来说,这个解决方案在我的项目中运作良好。

这个答案底部的解决方案是从几个Stack Overflow问题/答案中构建的。仅针对代码跳到那个。以下是冗长的细节。

在迁移类中使用SQL命令

我找不到仅使用AlterColumn()DropColumn()等实体框架迁移方法的解决方案。

我没有在Sql()方法中混合使用迁移方法和命令,而是使用了Sql()迁移方法中字符串中的所有SQL命令。使用所有SQL命令可以更轻松地在Visual Studio或SQL Server Management Studio的查询窗口中进行测试。

'Uchitha'的answer给出了在所需迁移类中添加Sql()“方法的开始步骤。”

  1. 使用“添加迁移”生成迁移类
  2. 使用类似于上面的代码
  3. 更改课程
  4. 使用Update-Database
  5. 运行迁移

    答案中的Sql()方法示例如下:

    Sql("UPDATE dbo.YourTable SET Column1 = 'VALUE1' "); 
    

    更改列类型 - 通用步骤

    我使用'{3}}'JustAnotherUserYouMayKnow'来开始更改列类型的步骤。我没有明确地遵循这一点,但它只提供了删除列并重新创建它的基本框架。

    1. 使用新类型添加新列
    2. 使用Sql()使用更新语句接管原始列中的数据
    3. 删除旧列
    4. 重命名新列
    5. 顺序GUID

      来自'Icarus'的answer提供了ALTER TABLE语句,使用newsequentialid()根据您的语句生成顺序GUID:

        

      我希望它是一个顺序递增的Guid。

      ALTER TABLE your_table
          ADD your_column UNIQUEIDENTIFIER DEFAULT newsequentialid() NOT null
      

      请注意'Icarus'在答案的评论部分中'Johan'的隐私问题:

        

      如果担心隐私,请不要使用newsequentialid()。可以猜测下一个生成的GUID的值,因此可以访问与该GUID相关联的访问数据

      更改主键

      要更改的列是ID列,您已将其设置为主键。因此,在删除现有ID列之前,您需要使用另一个ALTER TABLE SQL命令删除主键。

      请参阅“darnir”中的选定answer“我如何使用SQL语法更改主键约束?”

      ALTER TABLE <Table_Name>
      DROP CONSTRAINT <constraint_name>
      
      ALTER TABLE <Table_Name>
      ADD CONSTRAINT <constraint_name> PRIMARY KEY (<Column1>,<Column2>)
      

      请参阅'Oleg'的说明,以确定这是否是一个因素:

        

      PRIMARY KEY CONSTRAINT无法更改,您只能删除它并重新创建。对于大型数据集,它可能会导致运行时间较长,从而导致表不可用。

      执行上面DROP CONSTRAINT的命令时出现问题。结果窗格列出了自动生成的约束,即使我在ALTER TABLE ... ADD COLUMN命令中使用了特定的约束名称。请参阅此answer“为什么SQL会继续创建DF约束?”如果您遇到类似的事情,请question

      要通过删除约束解决问题,我使用了来自此question的'ScubaSteve'的答案:“如何在不知道其名称的情况下删除SQL默认约束?”通过'Seven'添加注释,这里是SQL命令:

      DECLARE @ObjectName NVARCHAR(100)
      SELECT @ObjectName = OBJECT_NAME([default_object_id]) FROM SYS.COLUMNS
      WHERE [object_id] = OBJECT_ID('[tableSchema].[tableName]') AND [name] = 'columnName';
      IF @ObjectName IS NOT NULL EXEC('ALTER TABLE [tableSchema].[tableName] DROP CONSTRAINT ' + @ObjectName)
      

      'ScubaSteve的答案中'Seven'的评论。我添加了'if'条件,因为当没有找到约束时,EXEC会失败。

        

      要使此脚本具有幂等性,请在EXEC命令

      之前添加IF @ObjectName IS NOT NULL

      最终解决方案

      请务必将以下代码中的MyTableNameMyColumnNamedbo替换为您的表名,列名(例如,将列名设置为Id)和表架构分别。

      public override void Up()
      {
          Sql(@"
              DECLARE @ObjectName NVARCHAR(100)
              SELECT @ObjectName = OBJECT_NAME([default_object_id]) FROM SYS.COLUMNS
              WHERE [object_id] = OBJECT_ID('[dbo].[MyTableName]') AND [name] = 'MyColumnName';
              IF @ObjectName IS NOT NULL EXEC('ALTER TABLE [dbo].[MyTableName] DROP CONSTRAINT ' + @ObjectName)
      
              ALTER TABLE dbo.MyTableName DROP CONSTRAINT PK_MyTableName, COLUMN MyColumnName
      
              ALTER TABLE dbo.MyTableName
              ADD Id UNIQUEIDENTIFIER DEFAULT (newsequentialid()) NOT NULL
              CONSTRAINT PK_MyTableName
              PRIMARY KEY CLUSTERED ([MyColumnName])
          ");
      }
      
      public override void Down()
      {
          Sql(@"
              DECLARE @ObjectName NVARCHAR(100)
              SELECT @ObjectName = OBJECT_NAME([default_object_id]) FROM SYS.COLUMNS
              WHERE [object_id] = OBJECT_ID('[dbo].[MyTableName]') AND [name] = 'MyColumnName';
              IF @ObjectName IS NOT NULL EXEC('ALTER TABLE [dbo].[MyTableName] DROP CONSTRAINT ' + @ObjectName)
      
              ALTER TABLE dbo.MyTableName DROP CONSTRAINT PK_MyTableName, COLUMN Id
      
              ALTER TABLE MyTableName
              ADD MyColumnName int IDENTITY(1, 1) NOT NULL
              CONSTRAINT PK_MyTableName
              PRIMARY KEY CLUSTERED ([MyColumnName] ASC)        
          ");
      }
      

答案 1 :(得分:2)

我找到了解决问题的简单方法。您只需删除列Guid,然后将其添加回public override void Up() { DropForeignKey("dbo.CustomField", "CustomFormId", "dbo.CustomForm"); DropForeignKey("dbo.CustomData", "CustomFieldId", "dbo.CustomField"); DropForeignKey("dbo.CustomForm", "ParentFormId", "dbo.CustomForm"); DropIndex("dbo.CustomField", new[] { "CustomFormId" }); DropIndex("dbo.CustomForm", new[] { "ParentFormId" }); DropIndex("dbo.CustomData", new[] { "CustomFieldId" }); DropPrimaryKey("dbo.CustomField"); DropPrimaryKey("dbo.CustomForm"); DropColumn("dbo.CustomField", "CustomFieldId") AddColumn("dbo.CustomField", "CustomFieldId", c => c.Guid(nullable: false)); AlterColumn("dbo.CustomField", "SortOrder", c => c.Int(nullable: false)); AlterColumn("dbo.CustomForm", "CustomFormId", c => c.Guid(nullable: false)); AlterColumn("dbo.CustomForm", "ParentFormId", c => c.Guid()); AddPrimaryKey("dbo.CustomField", "CustomFieldId"); AddPrimaryKey("dbo.CustomForm", "CustomFormId"); CreateIndex("dbo.CustomField", "CustomForm_CustomFormId"); CreateIndex("dbo.CustomForm", "ParentFormId"); CreateIndex("dbo.CustomData", "CustomField_CustomFieldId"); AddForeignKey("dbo.CustomField", "CustomForm_CustomFormId", "dbo.CustomForm", "CustomFormId"); AddForeignKey("dbo.CustomData", "CustomField_CustomFieldId", "dbo.CustomField", "CustomFieldId"); AddForeignKey("dbo.CustomForm", "ParentFormId", "dbo.CustomForm", "CustomFormId"); 列。这样就不会有任何错误消息,迁移将通过:

build.sbt