【问题标题】:Can you remove Identity from a primary key with Entity Framework 6?您可以使用 Entity Framework 6 从主键中删除身份吗?
【发布时间】:2017-12-04 10:13:01
【问题描述】:

我通过实体框架代码优先创建了一个表,主键设置为自动递增,但现在我想从列中删除该自动递增。我已经尝试使用流利的 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 数据库表中删除?

我还尝试如下在迁移中显式关闭身份,但也没有这样做:

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

还有其他方法告诉 SQL 删除身份吗?

【问题讨论】:

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


    【解决方案1】:

    正如其他人所说,EF 本身无法做到这一点,尽管它会创建一个迁移脚本,使其看起来像它具有的那样。我已经尝试了很多方法来做到这一点,我发现最简单的方法是:

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

    这应该会给你一个移除身份的迁移,并且可以成功地在数据库的其他副本上运行。

    注意:生成的Down 方法也不起作用,因为它将重新添加标识which EF also can't do。如果您需要 Down 方法来工作,请创建您添加的 SQL 的副本并对其进行调整以再次创建具有标识的表。

    上面的方法是@Georg Patscheider 在他的回答中描述得很好的一种快速方法。

    【讨论】:

    【解决方案2】:

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

    相反,您必须:

    • (备份数据库)
    • CREATE TMP_table 具有原始表的列,但 ID 列设置为标识: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)。

    【讨论】:

      【解决方案3】:

      如果您想从主键中删除身份,您将需要删除并重新创建表以及任何关联的外键。

      从列中删除标识规范只能通过重新创建表来完成。查看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

      【讨论】:

        【解决方案4】:

        如果你想删除身份,你需要

        第 1 步。以及对键列的注释

        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        
        public short Id { get; set; }
        

        这将生成迁移脚本

        AlterColumn("dbo.TableName", "Id", c => c.Long(nullable: false));
        

        但此脚本不会从键列中删除身份。

        步骤 2. 从 SQL Management Studio 的键列中手动删除标识。 您不需要备份数据库。

        如果出现错误,您可能需要转到“工具”->“选项”->“设计器”,并取消选中“防止保存需要重新创建表的更改”

        【讨论】:

        • 这听起来是个不错的解决方案。但对我来说,在 MS Management Studio 中,所有列属性都是灰色的,无法编辑。所以我不能改变(是身份)。这是对 MS SQL Express(我正在使用)的限制吗?
        • 这很有帮助。谢谢!!
        【解决方案5】:

        感谢这两个解释从列中删除身份所涉及的内容的答案,我想出了一个新版本的迁移,可以实现从 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");
            }
        }
        

        【讨论】:

          【解决方案6】:

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

          • 您不会丢失数据
          • 重新创建表需要很长时间(例如,您有数百万行,复制需要很长时间)
          • 您想用 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 列。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2020-02-11
            • 2021-04-30
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多