EFCore 通过 AddAsync() 添加临时 ID

Ill*_*lep 3 c# entity-framework-core

我正在使用 EFCore 5.0.0。

当我AddAsync (person);应该得到一个临时的时ID,我使用这个 ID 添加PersonIdfor School(如下面的代码所示)。最后,我将SaveChangesAsync()拯救一切。但是,PersonId设置为 0。我想改为临时ID存储。我怎样才能做到这一点。

await _dbContext.AddAsync(person);

School school = mySchool;
school.PersonId = person.Id;
await _dbContext.AddAsync(school);

await _dbContext.SaveChangesAsync();
Run Code Online (Sandbox Code Playgroud)

注意:有很多SO 帖子谈论临时 ID,但没有一个与本文相关。

Iva*_*oev 12

目前接受的答案是有效的,但技术上不正确。分配导航属性是有效的方法,但不是强制性的。甚至根本没有导航属性也是完全有效的。以及显式的 FK 属性。但至少总有影子 FK 属性可用于建立/维持关系。

因此临时密钥概念从一开始就是 EF Core 的一部分。然而 EF Core 3.0 引入了一项重大更改 -不再将临时键值设置到实体实例上。该链接包含对旧行为和新行为的解释、原因和可能的解决方案:

如果主键是存储生成的并且属于该状态中的实体,则将主键值分配到外键以形成实体之间的关联的应用程序可能依赖于旧行为Added。这可以通过以下方式避免:

  • 不使用商店生成的密钥。
  • 设置导航属性以形成关系,而不是设置外键值。
  • 从实体的跟踪信息中获取实际的临时键值。例如,context.Entry(blog).Property(e => e.Id).CurrentValue即使blog.Id尚未设置临时值,也会返回临时值。

项目符号#1 没有意义,项目符号#2 是另一个答案中建议的内容。项目符号 #3 是您问题的直接答案/解决方案。

将其应用到您的示例中只需更改

school.PersonId = person.Id;
Run Code Online (Sandbox Code Playgroud)

school.PersonId = _dbContext.Entry(person).Property(e => e.Id).CurrentValue;
Run Code Online (Sandbox Code Playgroud)

当然,当您拥有导航属性和相关实体实例时,最好使用它并让 EF Core 发挥其魔力。当您没有导航属性,或者您没有相关实体实例并且知道密钥,但不想进行往返以从数据库加载它时(并且使用假存根实体实例可以导致意想不到的副作用/行为)。它适用于显式 FK 属性和影子 FK 属性。