更改列的IDENTITY属性,需要删除并重新创建该列

时间:2018-11-21 08:44:19

标签: c# asp.net entity-framework entity-framework-core ef-core-2.1

我正在使用 EF Core 2.1

这是我最初的模型定义。

public class Customer //Parent
{
    public int Id { get; set; }

    public string Name { get; set; }

    public string Email { get; set; }

    public BankAccount BankAccount { get; set; }

}


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

    public string Branch { get; set; }

    public string AcntNumber { get; set; }

    public DateTime CreatedDate { get; set; }

    public int CustomerId { get; set; }

    public Customer Customer { get; set; }

}

但是我意识到同时拥有IdCustomerId是一对一的关系,因此我可以如下更新我的BankAccount模型定义。

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

    public string Branch { get; set; }

    public string AcntNumber { get; set; }

    public DateTime CreatedDate { get; set; }

    public Customer Customer { get; set; }

}

虽然在DbContext类中定义了以下主体实体。

HasOne(b => b.Customer).WithOne(c => c.BankAccount).HasForeignKey<BankAccount>(f => f.Id);

运行update-database时,出现以下错误。

  

System.InvalidOperationException:要更改列的IDENTITY属性,需要删除并重新创建该列。

但是,理想情况下,我不应该只是摆脱这个错误,而是删除列,约束以及表,然后删除完整的数据库。但是仍然是相同的错误。

14 个答案:

答案 0 :(得分:2)

我想比建议的“解决方案”要删除所有迁移,然后重新创建它,这意味着人们从来没有进入过生产环境,那么,我希望您已经解决了这个问题,但是,出于文档目的,我分享该链接说明了您所需要了解的所有有关密钥及其更改方法(无需删除迁移即可解决此问题)

https://thisworksonmymachine.com/2017/02/13/ef-core-the-setup-part-4/

另一个选项(不是我所做的)是从上一次迁移中删除主数据库的更改,然后从SQL手动删除它,在sql上手动添加新的主数据库,并且模型如何与您匹配不会出现问题。 / p>

答案 1 :(得分:1)

当您想要更改架构或已经存在的表(当EF核心不支持它但它需要手动操作)时,当您尝试更改或修改已经存在的表时,会发生此错误。这是您可以采取的措施:

  • 注释迁移文件中的相关代码,以避免此错误。
  • 或删除迁移文件并创建一个新文件。
  • 删除上游迁移,并让迁移生成新代码。

答案 2 :(得分:1)

当我尝试将模型从public byte Id {get; set;}更改为public int Id {get; set;}时遇到了这个问题。 面对这个问题,我做了以下事情:

  1. 删除所有迁移,直到在Package Manager控制台中使用Remove-Migration -Project <target_project>创建目标模型为止
  2. 删除实际数据库
  3. 如果中间有一些尚未创建的迁移(例如,它们来自另一个分支),则复制迁移文件以及ModelSnapshot文件并将其粘贴到分支中(仔细覆盖它们) !)。
  4. 在软件包管理器控制台中使用add-migration <migration_name>创建新迁移
  5. 在软件包管理器控制台中使用update-database更新数据库

我可以用这种方式解决它,因为我的代码不在生产环境中。如果模型已经存在,也许您就不得不面对另一个复杂的问题。

答案 3 :(得分:1)

在我的例子中,表 SharedBalances 被重命名为 Balances,它的标识列 SharedBalancesId 被重命名为 BalanceId。 SQL 命令在 SQL Server 上执行。你也可以试试migrationBuilder.Sql(my_sql_command_here)

我创建了迁移并遇到了同样的错误。

使用TSQL命令重命名列和表:

/*
migrationBuilder.RenameTable(
    name: "SharedBalances",
    newName: "Balances");
*/

在迁移中注释 RenameTable 命令:

/*
migrationBuilder.DropPrimaryKey(
    name: "PK_SharedBalances",
    table: "Balances");
migrationBuilder.AddPrimaryKey(
    name: "PK_Balances",
    table: "Balances",
    column: "BalanceId");
*/

注释迁移中的 AddPrimaryKey 命令:

        migrationBuilder.DropForeignKey(
            name: "FK_SharedBalances_Users_OwnerUserId",
            table: "SharedBalances");

        migrationBuilder.DropPrimaryKey(
            name: "PK_SharedBalances",
            table: "SharedBalances");

在迁移中更新出现的表名 DropForeignKey 命令:

从此....

        migrationBuilder.DropForeignKey(
            name: "FK_SharedBalances_Users_OwnerUserId",
            table: "Balances");

        migrationBuilder.DropPrimaryKey(
            name: "PK_SharedBalances",
            table: "Balances");

为此:

TopologyTestDriver

现在您的迁移将起作用。事情是这样发生的:

答案 4 :(得分:1)

问题有两部分答案:

TL;DR.:让 EF 为您做。

第一:让EF处理关系

简而言之,identity 属性是 DB 用来管理一个专用 ID 列的东西,所以它不依赖于任何外部的东西来识别每一行,因此 bankAccount 类需要它自己的 Id 字段来拥有 identity 属性;现在,如果您尝试手动告诉 EF 如何处理关系,就像您对线所做的那样

    HasOne(b => b.Customer).WithOne(c => c.BankAccount).HasForeignKey<BankAccount>(f => f.Id);

您所做的是覆盖 EF 本身的内部逻辑,在这种情况下,您告诉 EF 银行帐户 ID 是引用客户的字段,事实并非如此,因此 EF 尝试删除来自银行帐户模型中 id 字段的 IDENTITY,但这只是因为您告诉它银行帐户 ID 应该与客户 ID 相同。 我想我理解您想通过一对一关系表达的意思,但是当客户尝试开设另一个银行账户时会发生什么?乙: 相反,您应该保留 int CustomerId 字段并删除 Customer Customer 字段,不仅因为它更干净,而且因为只要有一个名为 {{1} 的类,EF 就会识别两者之间的关系}} 带有字段 Customer

Id

因此,客户可以根据需要在银行开设任意数量的账户,桌子不会受到影响,EF 知道该怎么做。

它会自动生成适当的外键并将其添加到迁移文件中,如下所示:

   public int CustomerId { get; set; } //keep

   public Customer Customer { get; set; } //delete

这将反映一对多的关系,这很好很花哨。

现在回答这个问题,如果你仍然想放弃这个属性:

第二:强制 EF 删除 Identity 属性

(但它不会解决原始问题的外键问题)。

首先,回顾一下:要更改身份属性,数据库管理器(mysql、sqlserver 等)将始终要求您删除并重新创建表,因为该字段是表的核心。所以你需要欺骗 EF 为你做一个解决方法。

  1. 将第二个 Id 元素添加到模型类,并使用类似 migrationBuilder.AddForeignKey( name: "FK_BankAccount_Customer_CustomerId", table: "BankAccount", column: "CustomerId", principalTable: "Customer", principalColumn: "Id", onDelete: ReferentialAction.Cascade); 的明显名称。

    duplicateId
  2. 这就是诀窍 ;)
    在实现 public class BankAccount { public int Id { get; set; } public int duplicateId { get; set; } ... } 接口的类中,添加以下方法,告诉 ef 您想要哪个字段主键是:

    DbContext
  3. 使用 protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<BankAccount>().HasKey(x => new { x.duplicateId }); } 添加一个新的迁移,因为这会更改表的主键并且迁移 $ dotnet ef migrations add ModelIdChange1 方法应该看起来像这样:

    Up
  4. 然后使用 protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.DropPrimaryKey( name: "PK_BankAccount", table: "BankAccountTableName"); migrationBuilder.AddColumn<int>( name: "duplicateId", table: "BankAccountTableName", type: "int", nullable: false, defaultValue: 0) .Annotation("SqlServer:Identity", "1, 1"); migrationBuilder.AddPrimaryKey( name: "PK_BankAccount", table: "BankAccountTableName", column: "duplicateId"); ... } 进行数据库更新(这些命令可能会有所不同,请使用您之前使用过的任何语法)。

  5. (可选)如果您需要保留原始 ID,请检查它们是否已保留,或者只是对表进行脏更新以将数据从 $ dotnet ef database update 字段复制到 {{1 }}。

  6. 现在原始的 Id 字段可以自由删除或更新,所以继续从模型中删除原始字段:

    duplicateId

如果您仍在尝试强制使用将 BankAccount Id 与客户的 Id 关联的原始命令,则在此步骤运行该命令应该可以正常工作,但请不要这样做。

  1. 并用Id添加一个新的Migration,然后用public class BankAccount { public int duplicateId { get; set; } ... } 做数据库更新,删除原来的$ dotnet ef migrations add ModelIdChange2列,留下$ dotnet ef database update作为主键。

  2. 现在,模型看起来几乎是原始模型,但是有了一个新的标识列,您可以保持原样,或者只是将 BankAccount 类中的字段从 Id 重命名为 duplicateId像这样:

    duplicateId

    并执行 Id,然后使用 public class BankAccount { public int Id { get; set; } ... } 执行数据库更新。

答案 5 :(得分:1)

  1. 表格没有重要数据
  • 重命名表 entity.ToTable("BankAccount2")
  • 添加运行迁移 Add-Migration BankAccountTempChanges
  • 更新数据库Update-Database
  • 重命名表 entity.ToTable("BankAccount")
  • 添加运行迁移 Add-Migration BankAccountOk
  • 再次更新数据库Update-Database
  1. 表中有数据不能丢失
  • 应用@Hani 回答中的解决方案

答案 6 :(得分:0)

我删除了Migrations文件夹和自动生成的_EFMigrationHistory保险箱。再次在软件包管理器控制台和Add-Migration命令上运行Update-Database MigrationName,问题已解决。

答案 7 :(得分:0)

我遇到了同样的问题,我通过两个步骤和两次迁移解决了这个问题:

  1. 删除身份列,在BankAccount中注释该ID并添加新的ID,即 以BankAccountId为身份,添加迁移和更新:此操作将删除ID并添加 新列作为标识。
  2. 删除新的“添加的”列,然后重新添加上一个,注释BankAccountId和取消注释ID,添加迁移和更新:这将删除BankAccountId并将ID添加为Idenity。

答案 8 :(得分:0)

在我看来,对除开发数据库以外的任何文件运行EF迁移会带来麻烦,因为您自然会受到以下事实的束缚:EF迁移有时会在更改对象的结构(更改主键和更改)时断然拒绝工作外键是最常遇到的问题。

多年来,我一直使用工具来确保数据库模式包含在版本控制中(与EF迁移互补)。做您的开发来更改您的dev数据库(数据并不重要),创建多个迁移,然后使用这些工具将它们汇总到数据库部署脚本中。

以下是我在这种情况下的操作摘要:-

  1. 删除(注释掉)对旧类BankAccount的所有引用
  2. 创建迁移并应用于开发数据库
  3. 使用正确的定义重新添加BankAccount
  4. 创建迁移并应用于开发数据库
  5. 使用数据库比较工具(我的首选是APEX SQL Diff,但市场上还有其他工具)来创建可汇总两个迁移的部署脚本。
  6. 在登台环境(应该有一些数据)上测试此脚本
  7. 如果测试合格,则适用于生产

现实情况是,如果您要使用代码优先方法从根本上改变生产结构的生产数据,那么除非您了解并解决了从一种结构到另一种结构的数据迁移问题,否则对您来说可能会很糟糕。

答案 9 :(得分:0)

我必须:

  1. 从代码中完全注释表格
  2. 运行从数据库清除的迁移
  3. 再次取消注释表并更正了映射
  4. 再次运行迁移

完成

答案 10 :(得分:0)

请按照以下步骤操作:

1-请在 sql server 中更改所有标识列(不在您的代码优先实体框架中)

迁移中的 2 条注释标识列更改(.cs 文件)

3-更新数据库

享受吧

答案 11 :(得分:0)

我遇到了同样的问题(就我而言,表中没有任何数据),我以这种方式解决了它(这不是正确的方法,但对我有用):

  1. 我已从 EFCore 项目中手动删除了迁移。我也删除了从文件 _ContextModelSnapshot 中添加的那些行。 (我有一个已应用的迁移和一个已创建但未应用的迁移,因为我收到错误 - 更改列的 IDENTITY 属性,需要删除并重新创建该列 )
  2. 我已经手动删除了在数据库中创建的表(第一次迁移)
  3. 我删除了表 _EFMigrationHistory 中与我想要删除的迁移相关的行。
  4. 重新运行 VS
  5. 添加迁移 NewOneCleanMigration
  6. 更新数据库

答案 12 :(得分:0)

对于像我这样懒惰的人:您想将主键列 "Id" 的数据类型从 int 更改为 Guid在一个名为“翻译”的表格中,就像我的情况一样。

  1. 在这种情况下您生成的迁移是
<块引用>
migrationBuilder.AlterColumn<Guid>(
     name: "Id",
     table: "Translations",
     type: "uniqueidentifier",
     nullable: false,
     oldClrType: typeof(int),
     oldType: "int")
     OldAnnotation("SqlServer:Identity", "1, 1");

您可以删除或评论该内容

  1. 来自更新数据库错误System.InvalidOperationException:要更改列的 IDENTITY 属性,需要删除并重新创建该列
  2. 我们也知道,如果不先删除主键约束,就不能删除列。我们的新迁移成为
<块引用>
migrationBuilder.DropPrimaryKey(
    name: "PK_Translations",
    table: "Translations");
<块引用>
  migrationBuilder.DropColumn(
    name: "Id",
    table: "Translations");
<块引用>
  migrationBuilder.AddColumn<Guid>(
    name: "Id",
    table: "Translations",
    type: "uniqueidentifier",
    nullable: false);

记得在 Down override 方法中做相反的事情,以防你可能想要反向迁移

答案 13 :(得分:0)

我遇到了类似的问题,我将表配置的关系导航组件从 WithMany 更改为 WithRequiredDependent。实体框架想要删除索引并重新创建列,即使数据库中的任何内容都不应该改变。

为了解决这个问题,我重新搭建了最新的迁移,它允许实体在不创建任何新迁移的情况下吸收更改。您可以通过从目标数据库恢复迁移并使用完全相同的迁移名称为最新迁移重新运行 Add-Migration 脚本来重新构建最新迁移。