您是否可以使用Entity Framework 6从主键中删除Identity?

时间:2017-12-04 10:13:02

标签: entity-framework sql-server-2008 entity-framework-6

我通过实体框架代码创建了一个表,首先将主键设置为自动递增,但现在我想从列中删除该自动递增。我已尝试使用流畅的API:

public class ProductTypeMap: EntityTypeConfiguration<ProductType>
{
    public ProductTypeMap()
    {
        // This is an enum effectively, so we need fixed IDs
        Property(x => x.ProductTypeId)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
    }

}

和注释:

public class ProductType
{
    [Required, Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int ProductTypeId { get; set; }
    public string ProductTypeName { get; set; }

}

在这两种情况下,他们都会生成相同的迁移代码:

public partial class removeproducttypeidentity : DbMigration
{
    public override void Up()
    {
        DropPrimaryKey("dbo.ProductTypes");
        AlterColumn("dbo.ProductTypes", "ProductTypeId", c => c.Int(nullable: false));
        AddPrimaryKey("dbo.ProductTypes", "ProductTypeId");
    }

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

但是,当我在数据库上运行迁移时,身份规范不会从SQL Server 2008数据库表中删除吗?

我还尝试在迁移过程中明确关闭Identity,如下所示,但是也没有这样做:

    AlterColumn("dbo.ProductTypes", "ProductTypeId", c => c.Int(nullable: false, identity: false));

是否有另一种方法告诉SQL删除标识?

5 个答案:

答案 0 :(得分:1)

您无法使用ALTER COLUMN来设置列是否为标识列(How to alter column to identity(1,1))。

相反,你必须:

  • (备份数据库)
  • CREATE TMP_table包含原始列的列,但ID列设置为identity:false
  • SET IDENTITY_INSERT [TMP_Table] ON
  • 将数据从原始数据复制到TMP表
  • SET IDENTITY_INSERT [TMP_Table] OFF
  • DROP原始表
  • 将TMP_table重命名为原始表名(EXECUTE sp_rename

提示:更改SQL Management Studio中的列并检查发出的脚本(SQL SERVER – Add or Remove Identity Property on Column)。

答案 1 :(得分:1)

如果要从主键中删除标识,则需要删除并重新创建表及任何关联的外键。

只能通过重新创建表来从列中删除标识规范。查看ALTER TABLE - ALTER COLUMN语句:没有语法可以更改(添加或删除)标识规范。

参考流动网站:

Cant remove identity attribute from PK

Entering keys manually with Entity Framework

EF6: Switching Identity On/Off with a Custom Migration Operation

Remove Identity from Primary Key with Drop and Recreate Table

答案 2 :(得分:0)

由于这两个答案解释了从列中删除标识所涉及的内容,我想出了一个新版本的迁移,它实现了从PK列中删除标识。

此解决方案会丢失表格中的所有数据。这在我的情况下是可以接受的,因为所讨论的表是一个表示枚举的表,因此由我的种子方法手动填充(我首先删除了身份,因此我可以指定用于PK Id字段的值)。 请勿对用户输入的数据使用此方法

public partial class removeproducttypeidentity : DbMigration
{
    public override void Up()
    {
        DropForeignKey("dbo.Products", "ProductTypeId", "dbo.ProductTypes");
        DropPrimaryKey("dbo.ProductTypes");

        DropTable("dbo.ProductTypes");

        CreateTable(
                "dbo.ProductTypes",
                c => new
                {
                    ProductTypeId = c.Int(nullable: false),
                    ProductTypeName = c.String(),
                })
            .PrimaryKey(t => t.ProductTypeId);

        AddForeignKey("dbo.Products", "ProductTypeId", "dbo.ProductTypes", "ProductTypeId");
    }

    public override void Down()
    {
        DropForeignKey("dbo.Products", "ProductTypeId", "dbo.ProductTypes");
        DropPrimaryKey("dbo.ProductTypes");

        DropTable("dbo.ProductTypes");

        CreateTable(
                "dbo.ProductTypes",
                c => new
                {
                    ProductTypeId = c.Int(nullable: false, identity: true),
                    ProductTypeName = c.String(),
                })
            .PrimaryKey(t => t.ProductTypeId);

        AddForeignKey("dbo.Products", "ProductTypeId", "dbo.ProductTypes", "ProductTypeId");
    }
}

答案 3 :(得分:0)

正如其他人所说,EF不能本地执行此操作,尽管它会创建一个使其看起来像它的迁移脚本。我已经尝试了很多方法,我发现最简单的方法是:

  1. 备份数据库......
  2. 更改您的类,使其不再具有代码中的标识(通过删除属性或映射)
  3. 使用程序包管理器控制台(add-migration [your migration name]
  4. 生成迁移
  5. 在新生成的迁移
  6. 中注释Up方法中的代码
  7. 添加一行新代码,以便接收您将在下面生成的SQL:Sql (@" ");
  8. 进入SSMS并确保在进行表格更改时将其设置为生成脚本
  9. 删除SMSS中的表设计器中的标识
  10. 在SSMS中保存表更改并复制生成的SQL。 (该SQL使用数据创建表的副本,删除原始表,然后重新创建原始表而不在列上设置标识,然后将所有数据复制回来并再次添加所有外键和约束)< / LI>
  11. 将您刚刚复制的SQL粘贴到上面添加的代码中的语音标记之间。
  12. 运行迁移
  13. 然后应该为您提供删除身份的迁移,并且可以成功地在数据库的其他副本上运行。

    注意:生成的Down方法无效,因为它将重新添加身份which EF also can't do。如果您需要Down方法来创建您添加的SQL的副本并进行调整以再次创建具有该标识的表。

    上述方法可以快速了解@Georg Patscheider在答案中所描述的内容。

答案 4 :(得分:0)

此解决方案适用于以下情况:

  • 您不会丢失数据
  • 重新创建表需要很长时间(例如,您有数百万行,复制需要很长时间)
  • 您想用 EF 处理所有事情,而不是编写自定义迁移(@Georg Patscheider 和 @tomRedox 的方法绕过 EF)
    • 这是个好主意,因为 EF 编写的未来迁移不太可能出现问题
      • (在我的数据库中,我有数百个迁移,其中一些带有自定义 SQL,这在最初编写这些迁移数年后给我带来了麻烦,因为 EF 不知道它们!)立>

解决办法

  1. 创建一个新列并将所有旧 ID 复制到其中
  2. 将新列设为主键
  3. 将对旧列的所有引用更新到新列

设置

示例实体:

public class Foo
{
    [Key]
    public int Id { get; set; }
}

public class Bar
{
    // This is the foreign key:
    public int FooId { get; set; }
    public virtual Foo Foo { get; set; }
}

添加一个将身份设置为 false 的新属性

Foo 更改为:

public class Foo
{
    [Key]
    public int Id { get; set; }

    [Index(IsUnique = true)]
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int NewId { get; set; }
}

生成迁移:

public partial class AddNewId : DbMigration
{
    public override void Up()
    {
        AddColumn("dbo.Foos", "NewId", c => c.Int(nullable: false));

        // This is a custom line which copies the data from the old column to the new.
        Sql("UPDATE dbo.Foos SET NewId = Id");

        CreateIndex("dbo.Foos", "NewId", unique: true);
    }
        
    public override void Down()
    {
        DropIndex("dbo.Foos", new[] { "NewId" });
        DropColumn("dbo.Foos", "NewId");
    }
}

将引用从 Foo.Id 更改为 Foo.NewId

这是棘手的部分。

Foo 实体的​​任何引用仍在使用旧的 Id 属性而不是新的 NewId 属性。

使用 Visual Studio:

  1. 暂时注释掉旧的 Foo.Id 属性
  2. 手动将 Foo.NumericId 的名称更改为 Foo.Id(不更新引用)
  3. Foo.Id 重命名回 Foo.NumericId 但让 VS 也更改所有引用
  4. 在第 1 步中注释掉的旧 Foo.Id 属性中重新注释

Foo.NewId 设为主键

public class Foo
{
    public int Id { get; set; }

    [Key]
    [Index(IsUnique = true)]
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int NewId { get; set; }
}

更新Bar.FooId

所有实体现在都需要引用 Foo.NewId 而不是 Foo.Id

更改Bar

public class Bar
{
    // This is the foreign key:
    public int FoodNewId { get; set; }
    public virtual Foo Foo { get; set; }
}

创建一个新的迁移:

public partial class AddNewIdPart2 : DbMigration
{
    public override void Up()
    {
        DropForeignKey("dbo.Bars", "FooId", "dbo.Foos");
        RenameColumn(table: "dbo.Bars", name: "FooId", newName: "FooNewId");
        DropPrimaryKey("dbo.Foos");
        AddPrimaryKey("dbo.Foos", "NewId");
        AddForeignKey("dbo.Bars", "FooNewId", "dbo.Foos", "Newid");
    }
        
    public override void Down()
    {
        // ...
    }
}

完成!

这应该就是一切。

如果需要,您现在可以删除旧的 Foo.Id 列。