【问题标题】:EF Core, nullable primary key results into inability to delete not-yet-added entityEF Core,可为空的主键导致无法删除尚未添加的实体
【发布时间】:2020-07-30 13:48:11
【问题描述】:

我使用的是 EF Core 2.2.4。

这是我的实体:

public class Person
{
    public int? Id { get; set; }
    public string Name { get; set; }
    public string Surname { get; set; }
}

注意Person 有一个可为空类型的主键。这似乎由 EF Core 优雅地处理,最终实体获得了他们的 ID;我使用术语“nullable-type”PK 而不是“nullable PK”,因为其目的不是插入由数据库生成的具有 null PK 的记录,而是为了正确处理密钥还没有的实体的情况已生成。
正如我所说,一代似乎运作良好。但并非一切都运行良好。

AppDbContext dbc = new AppDbContext();
Person p1 = new Person() { Name = "Isaac", Surname = "Newton" };
dbc.Add(p1);
dbc.Remove(p1);
dbc.SaveChanges();

如果我运行此代码,dbc.Remove(p1) 行无法正常工作,实际上我收到此错误:

System.ArgumentNullException: '值不能为空。参数名称:key'

如果我将 ID 从 int? 更改为 int,则上述代码有效。

对于可为空类型的键,临时(负)Id 似乎不会分配给 Id。

Nullable-type键有一些优点,主要是在Save阶段不显示看似懒惰生成的临时Id。

如果这是一个受支持的功能并且我看到的是一个错误,或者更确切地说这只是一个不受支持的功能,我在文档中找不到,我应该避免使用可空类型的主键。

【问题讨论】:

  • 你为什么要添加然后删除?
  • 对比0而不是null有什么问题?
  • 如果您不想保留更改,请不要致电SaveChanges。确保在 using 块中也使用 DbContext - 它的生命周期很短,仅用于工作单元。如果您不调用 SaveChanges(),则所有修改都将在 DbContext 被释放时被丢弃
  • 所有这些都是设计使然,你不想要一个 int 吗?代码中的 id 除非您的数据库也允许 null(并且在您编写时,它不允许)
  • BTW Id 不应该可以为空。表的 P​​K 不能为空。如果您想使用自动生成的 ID,只需保留其默认值 0。当 ID 为 0 时,EF Core(以及所有 ORM)将生成一个 INSERT,如果它不为零,则更新,除非实体是标记为新而不是修改

标签: c# entity-framework ef-core-2.2


【解决方案1】:

您不应使用 int? 作为您的 Id,因为当您想要删除它时它将为空,并且 EFCore 将无法找到正确的值并抛出此异常,因为真正的 Id 是由dbc.SaveChanges(); 之后的数据库。我建议使用普通的int,因为 EFCore 会为您的条目提供一个临时 ID,该 ID 将被您的数据库覆盖。

还有一些数据库是 EFCore 生成密钥而不是数据库本身,我猜 EFCore 认为不需要生成可空值或在 dbc.SaveChanges() 之后生成。

现在为什么因为主键中的null而导致remove痛苦地尖叫?!简单的答案:在 SQL NULL = NULL equals false 和 EFCore 中只会向您抛出一个错误,因为如果它要求 Is Null 它可能会删除您不想删除的实体。

You can find more Information there...

【讨论】:

    【解决方案2】:

    您示例中的人是 DTO。 显然,ID 被用作数据库中的主键(PK)。 根据定义,数据库中的 PK 不能为空。 因此,PK 上的 DTO 和 DB 约束不匹配(非空)。 除非您的数据库允许否则,这是由于错误而不是这种情况。

    它在您获取数据、代码方向时起作用(可以为空的可以保持值,是的)。或者,删除命令不会超过数据库中设置的 PK 值约束(当值为空时)。 因此错误被传播。

    突出显示 DTO 与其数据源不匹配。

    但是,您可以将模型映射到 DTO(例如,使用 AutoMapper)。 如果您在尝试实现的业务逻辑中需要它,那么该模型可以具有可为空的 id。 建议处理空值。

    这样 DB 操作将使用 DTO 执行(其中 Id 设置为非空,即在您的情况下为 int )。 并且业务逻辑操作将使用 Model 执行(其中 Id 可以为空,即 int?)。

    这为您提供了可空 id 的优势,但同时与 db 配合得很好。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-14
      • 1970-01-01
      • 2021-03-27
      • 1970-01-01
      • 1970-01-01
      • 2019-11-26
      相关资源
      最近更新 更多