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

Kgn*_*web 11 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; }

}
Run Code Online (Sandbox Code Playgroud)

但是我意识到同时拥有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; }

}
Run Code Online (Sandbox Code Playgroud)

而在DbContext类中,定义主体实体如下。

HasOne(b => b.Customer).WithOne(c => c.BankAccount).HasForeignKey<BankAccount>(f => f.Id);
Run Code Online (Sandbox Code Playgroud)

运行时,update-database我得到以下错误。

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

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

Tit*_*iva 8

我有这个问题时,我试图从改变一个模型public byte Id {get; set;}public int Id {get; set;}。为了解决这个问题,我做了以下几件事:

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

我可以这样解决,因为我的代码不在生产环境中。如果模型已经在那里,也许你不得不面对另一个复杂的问题。

  • 虽然如果您处于开发环境中,此策略完全有效,但如果您处于预生产环境中,则会出现问题。 (16认同)

小智 7

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

步骤1

  1. 删除标识列。
  2. 注释BankAccount中的ID并添加一个新ID(即,将BankAccountId作为
    标识,添加迁移和更新-这将删除ID)。
  3. 添加一个新列作为标识。

第2步

  1. 删除新添加的列,然后重新添加上一列。注释BankAccountId和取消注释ID。
  2. 添加迁移和更新(这将删除BankAccountId并将ID添加为标识)。

  • 仅适用于没有外键的超级简单场景。这并不是一个真正可行的解决方案。 (5认同)

小智 7

我不得不:

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

完毕

  • 仅适用于没有外键的超级简单场景。这并不是一个真正可行的解决方案。 (3认同)

unJ*_*rdi 7

这个问题有两部分答案:

TL;DR.:让 EF 为您做这件事。

第一:让 EF 处理关系

简而言之,identity属性是数据库用来管理专用ID列的,因此它不依赖于自身之外的任何东西来识别每一行,因此bankAccount类需要它自己的Id字段来具有identity属性;现在,如果您尝试手动告诉 EF 如何处理关系,就像处理该行一样

    HasOne(b => b.Customer).WithOne(c => c.BankAccount).HasForeignKey<BankAccount>(f => f.Id);
Run Code Online (Sandbox Code Playgroud)

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

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

   public Customer Customer { get; set; } //delete
Run Code Online (Sandbox Code Playgroud)

这样,客户就可以在银行开设任意数量的账户,桌子不会受到影响,并且 EF 知道该怎么做。

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

    migrationBuilder.AddForeignKey(
            name: "FK_BankAccount_Customer_CustomerId",
            table: "BankAccount",
            column: "CustomerId",
            principalTable: "Customer",
            principalColumn: "Id",
            onDelete: ReferentialAction.Cascade);
Run Code Online (Sandbox Code Playgroud)

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

现在,回答这个问题,如果您仍然想删除该属性:

第二:强制 EF 删除 Identity 属性

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

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

  1. 将第二个 Id 元素添加到模型类,其名称很明显,例如duplicateId

    public class BankAccount
    {
        public int Id { get; set; }
        public int duplicateId { get; set; }
        ...
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 这就是窍门;)
    在您实现接口的类中DbContext,添加以下方法,告诉 ef 您希望主键是哪个字段:

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<BankAccount>().HasKey(x => new { x.duplicateId });
    }
    
    Run Code Online (Sandbox Code Playgroud)
  3. 添加一个新的迁移$ dotnet ef migrations add ModelIdChange1,因为这会更改表的主键,并且迁移Up方法应该如下所示:

    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");
    ...
    }
    
    Run Code Online (Sandbox Code Playgroud)
  4. 然后执行数据库更新$ dotnet ef database update(这些命令可能会有所不同,使用您之前使用过的任何语法)。

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

  6. 现在原始Id字段可以自由删除或更新,因此只需从模型中删除原始字段即可:

    public class BankAccount
    {
        public int duplicateId { get; set; }
        ...
    }
    
    Run Code Online (Sandbox Code Playgroud)

如果您仍在尝试强制使用将 BankAccount Id 与客户 ID 链接的原始命令,则在此步骤运行该命令应该会起作用,但请不要这样做。

  1. 添加新的迁移$ dotnet ef migrations add ModelIdChange2,然后使用 进行数据库更新$ dotnet ef database update,这将删除原始Id列并保留duplicateId为主键。

  2. 现在,该模型看起来几乎是原始模型,但有了一个新的标识列,您可以保留它,或者只是在 BankAccount 类中将该字段重命名为duplicateIdto Id,如下所示:

    public class BankAccount
    {
        public int Id { get; set; }
        ...
    }
    
    Run Code Online (Sandbox Code Playgroud)

    并执行$ dotnet ef migrations add ModelIdChange3,然后使用 进行数据库更新$ dotnet ef database update


Pay*_*jad 5

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

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