EFCore通过AddAsync()方法添加临时ID。

3
我正在使用EFCore 5.0.0。
当我执行AddAsync(person);时,应该会获取一个临时的ID,然后我会使用这个ID来添加SchoolPersonId(如下所示)。最后,我将使用SaveChangesAsync()保存所有内容。但是,PersonId被设置为0。我想获取存储的临时ID。我该如何做?
await _dbContext.AddAsync(person);

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

await _dbContext.SaveChangesAsync();

注意:有许多关于临时ID的SO帖子,但没有一个与这篇文章相关。

根据这篇文章,你的模型配置是否正确?https://thesharperdev.com/ef-cores-addasync-v-add-method/ - undefined
2个回答

10

目前被接受的答案是有效的,但在技术上是不正确的。分配导航属性是一种有效的方法,但并非强制性的。甚至完全没有导航属性也是完全有效的。以及显式的FK属性。但始终存在至少一个影子FK属性,可用于设置/维护关系。

因此,临时键概念是EF Core从一开始就具备的一部分。然而,EF Core 3.0引入了一个破坏性变化-临时键值不再设置到实体实例上。该链接包含旧行为和新行为的解释,原因和可能的解决方案:

将主键值分配到外键以形成实体之间的关联的应用程序,如果主键是存储生成的并且属于处于“Added”状态的实体,则可能依赖旧行为。可以通过以下方式避免这种情况:
  • 不使用存储生成的键。
  • 设置导航属性以形成关系,而不是设置外键值。
  • 从实体的跟踪信息中获取实际的临时键值。例如,context.Entry(blog).Property(e => e.Id).CurrentValue将返回临时值,即使blog.Id本身尚未设置。
第一条建议没有意义,第二条是其他答案中提出的建议。第三条是您问题的直接答案/解决方案。
将其应用于您的示例只需要更改:
school.PersonId = person.Id;

school.PersonId = _dbContext.Entry(person).Property(e => e.Id).CurrentValue;

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

1
嗨,我刚刚试图提交一个关于你示例代码中的小错误的编辑,但是SO不允许少于6个字符的编辑。我不太愿意修改你的其他答案以适应这个奇怪的限制,所以我在这里联系你。你在第二个代码块中错误地写成了"_contexy"而不是"_context"。如果这是有意的,请忽略。否则,现在你知道了。 - undefined
嗨@Daniel,当然不是故意的,谢谢你指出来。干杯。 - undefined

3

我从未见过在EF Core中使用临时id来连接实体。

通常情况下,您会分配实体,并让EF处理id和关系。

也就是说,在这种情况下,School将链接到Person

await _dbContext.AddAsync(person);

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

await _dbContext.SaveChangesAsync();

当保存时,PersonId列会被正确的Id填充吗? - undefined
@Illep 是的,应该这样做。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接