使用列表的子记录保存记录时出现Entity Framework错误。
使用列表的子记录保存记录时出现Entity Framework错误。
在我的ASP.NET Core项目中,我们使用EF Core。当保存一个具有子记录列表的记录时,我们会遇到以下错误:
实体类型Child
的实例无法被跟踪,因为已经有另一个具有相同键值{'Id'}的实例被跟踪。
步骤如下:
1. 从控制器中获取记录并传递给视图(视图具有一个)
2. 通过HTML表单更新一些属性
3. 点击保存
4. 在控制器的Save方法中捕获传递的模型。从数据库中获取原始项目并保存更改(通过表单进行的更改)
5. 在存储库中调用更新/保存操作
简化的代码如下:
// 从数据库中获取记录 var record = _dbContext.Parents .Include(x => p.SomeOtherObject) .Include(x => x.ListChildren) .FirstOrDefault(x => x.IdParent == id); // 对Parent和ListChildren进行一些更改 // 对SomeOtherObject不进行任何更改!!! // 保存更改 _dbContext.Update(record); _dbContext.SaveChanges(); // 实体定义 public class Parent { public int IdParent { get; set; } public string Name {get; set;} public string Surname {get; set;} public int IdSomeOtherObject { get; set; } [ForeignKey("IdSomeOtherObject")] public virtual SomeOtherObject SomeOtherObject { get; set; } public virtual ListListChildren { get; set; } } public class Child { public int IdChild { get; set; } public int IdParent { get; set;} public string Name { get; set; } } public class SomeOtherObject { public int IdSomeOtherObject { get; set; } public string PropertiesBlahBla { get; set; } }
现在,我知道我们可以在Get
操作中添加.AsNoTracking()
,但问题是当保存Parent
时,EntityFramework会针对SomeOtherObject
执行UPDATE
SQL语句(即使它没有以任何方式更改),这对于我们的数据/输入场景是不可接受的。
是否有其他方法可以解决这个错误?
问题的出现原因是在保存具有子记录列表的记录时出现了EntityFramework错误。解决方法是尝试移除_dbContext.Update(record);
代码,因为实体应该已经被跟踪,所以改动会被保存。根据Update
文档所述,它会开始跟踪给定的实体和可从给定实体访问的条目,默认使用Modified状态。因此在这种情况下,它是不需要的。
在讨论中发现,子跟踪集合是这样替换的:
record.ListChildren = someModel.ListChildren.Where(...).ToList()
。这导致了添加具有已被跟踪的ID的元素。所以应该按照建议的方法更新ListChildren
,像这里建议的一样。
完整的错误信息如下:'The instance of entity type '' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.'
你的数据库上下文是否有范围?还要检查在调用之前context.ChangeTracker.Entries()
是否为空。另外,你能否提供一个最小可复现的示例?
在startup.cs
中,我们使用services.AddScoped<xyz, xyz>();
来添加服务/仓储。
当尝试检查context.ChangeTracker.Entries()
时,返回的是与我之前的问题相同的错误。
如果你在获取record
之前尝试访问它,这意味着错误出现在提供的代码之外(之前)。
整个解决方案/项目非常庞大,所以很难准备一个最小可复现的示例,特别是没有数据库连接的情况下...我将在上面的代码示例中添加一些额外的信息。
我尝试在获取和更新/保存更改之前访问context.ChangeTracker.Entries()
。请让我们在聊天中继续讨论。