我正在 Blazor 服务器中使用 EF Core 处理表单。我在实体跟踪方面遇到了许多问题,因此我将所有查询设置为 AsNoTracking,并将我的服务设计为为每个查询创建一个新的 dbcontext 实例。我认为这是合适的,因为不会编辑任何返回值 - 只会存储用户输入的表单数据和对查询字段(例如员工编号)的 id 引用。对于插入数据,我使用 this:
using var context = Factory.CreateDbContext();
context.SetupForm.Attach(model);
context.Entry(model).State = EntityState.Added;
await context.SaveChangesAsync();
我是附加数据而不是添加数据,然后将表单对象状态设置为已添加。这可确保 EF Core 在插入表单数据时不会尝试插入现有员工对象。
问题始于表单的一部分,该部分可以包含用户想要的任意数量的项目。选择几个员工并输入相关数据。当他们提交表单时,他们可能在多个项目中选择了同一名员工。由于这些员工是从不同的上下文中选择的,因此它们是具有相同 ID 的两个独立实例。当然,EF Core 不喜欢这样,会抛出这样的错误:
The instance of entity type 'Principal' cannot be tracked because another instance with the key value '{EmployeeID: 1234}' is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.
我理解为什么会发生此错误,但我需要能够以这种方式附加多个实体。我该如何解决这个问题?
我可以做的一件事是手动分配外键,但这会很严格,并且需要在模型更改时进行更新。
答案 0 :(得分:0)
试试这个
using var context = Factory.CreateDbContext();
context.Set<Principal>().Add(model);
//or maybe context.Principals.Add(model);
await context.SaveChangesAsync();
答案 1 :(得分:-1)
这似乎奏效了!它的作用是将缺少密钥的任何实体标记为已添加。否则,该实体将被完全忽略。
using var context = Factory.CreateDbContext();
context.ChangeTracker.TrackGraph(model, node =>
{
if (!node.Entry.IsKeySet)
{
node.Entry.State = EntityState.Added;
}
});
await context.SaveChangesAsync();
不需要插入任何有键的项目。将它们视为未跟踪,然后解决任何重复的问题,并只插入需要它的行。
更多信息:https://docs.microsoft.com/en-us/ef/core/change-tracking/identity-resolution#resolve-duplicates