当使用EF Core对同一数据实体进行连续更新时,有时会发生跟踪实体异常,即便使用EF Core的数据仓储查询数据时已经设置了DetachedEntities(不跟踪的脱轨实体),依旧会发成此错误。
我一度以为是使用了异步方法的原因,后来查询updateNow返回的消息后,发现实体状态Sate为unChanged,说明此数据还未被更新到数据库,EF Core依旧保持着对该实体的跟踪状态.
_mainBill.UpdateNow(mainBill).State
查询State的值后,发现此参数是一个枚举值,它包含了(该枚举类来自EF Core对EntityState的反编译):
public enum EntityState
{
//
// 摘要:
// The entity is not being tracked by the context.
Detached,
//
// 摘要:
// The entity is being tracked by the context and exists in the database. Its property
// values have not changed from the values in the database.
Unchanged,
//
// 摘要:
// The entity is being tracked by the context and exists in the database. It has
// been marked for deletion from the database.
Deleted,
//
// 摘要:
// The entity is being tracked by the context and exists in the database. Some or
// all of its property values have been modified.
Modified,
//
// 摘要:
// The entity is being tracked by the context but does not yet exist in the database.
Added
}
查阅资料并进行几次尝试后,在执行UpdateNow操作后,通过获取当前数据实体的数据库上下文,并设置对该实体不再跟踪,连续的EF Core更新同一数据实体就不在抛出跟踪异常了。
示例代码如下:
第一种方法:数据仓储更新数据,手动设置数据库上下文取消对当前实体的跟踪
_mainBill.UpdateNow(mainBill);
_mainBill.Context.Entry(mainBill).State = EntityState.Detached;//更新后取消跟踪
第二种方法:完全由数据库上下文执行更新操作
//获取当前数据库上线文(_mainBill是数据仓储IRepository<MainBill>),也可以直接使用你自己设定的
//databaseContext
var Context=_mainBill.Context;
Context.Attach<MainBill>(mainBill);//添加数据实体到数据库上下文中
Context.Entry<MainBill>(mainBill).State = EntityState.Modified;//修改操作
Context.SaveChanges();//提交保存更改
Context.Entry<MainBill>(mainBill).State = EntityState.Detached;//取消跟踪
虽然这里设施数据库上下文不在跟踪实体,但不会影响当前的数据更新操作,只是指定EF Core不在对此实体进行跟踪。
以下是我查询ChatGpt给出的资料:
这个错误通常发生在EF Core上下文中重复跟踪相同键值的实体时。为了解决这个问题,您可以尝试以下几种方法:
1.使用
AsNoTracking()
方法:在第二次调用UpdateNow()
之前,使用AsNoTracking()
方法来禁用对实体的跟踪。这样可以确保第二次更新操作不会与之前跟踪的实体冲突。// 更新操作 context.EcOrder.AsNoTracking().UpdateNow(updatedEcOrder); // 第二次更新操作 context.EcOrder.AsNoTracking().UpdateNow(anotherUpdatedEcOrder);
2.分离实体对象:在第一次更新操作完成后,使用
Detach()
方法将实体对象从上下文中分离,然后进行第二次更新操作。// 更新操作 context.EcOrder.UpdateNow(updatedEcOrder); // 分离实体对象 context.Entry(updatedEcOrder).State = EntityState.Detached; // 第二次更新操作 context.EcOrder.UpdateNow(anotherUpdatedEcOrder);
3.创建新的上下文实例:如果以上方法无效,您可以考虑创建一个新的上下文实例来执行第二次更新操作。这样可以确保每个更新操作都在独立的上下文中进行,避免实体跟踪冲突。
// 第一个上下文实例 using (var context = new YourDbContext()) { // 更新操作 context.EcOrder.UpdateNow(updatedEcOrder); } // 第二个上下文实例 using (var anotherContext = new YourDbContext()) { // 第二次更新操作 anotherContext.EcOrder.UpdateNow(anotherUpdatedEcOrder); }