查询时已加入AsNoTracking设置不追踪,为什么UpdateRange更新时还是会报tracked的相关错误?
错误信息
异常信息:The instance of entity type 'Wxuserdatum' 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.
异常明细: at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.ThrowIdentityConflict(InternalEntityEntry entry)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.Add(TKey key, InternalEntityEntry entry, Boolean updateDuplicate)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.StartTracking(InternalEntityEntry entry)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState(EntityState oldState, EntityState newState, Boolean acceptChanges, Boolean modifyProperties)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState(EntityState entityState, Boolean acceptChanges, Boolean modifyProperties, Nullable`1 forceStateWhenUnknownKey, Nullable`1 fallbackState)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityGraphAttacher.PaintAction(EntityEntryGraphNode`1 node)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityEntryGraphIterator.TraverseGraph[TState](EntityEntryGraphNode`1 node, Func`2 handleNode)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityGraphAttacher.AttachGraph(InternalEntityEntry rootEntry, EntityState targetState, EntityState storeGeneratedWithKeySetTargetState, Boolean forceStateWhenUnknownKey)
at Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.SetEntityState(InternalEntityEntry entry, EntityState entityState)
at Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.SetEntityStates(IEnumerable`1 entities, EntityState entityState)
at Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.UpdateRange(IEnumerable`1 entities)
at Tencent.DB.WxWorkSyncHelper.PullUser() in E:\KangShuaiFu\Code\ShenGu\KaoQinXiTong\Code\Tencent.DB\WxWorkSyncHelper.cs:line 187:
代码
var cacheUsers = _mysql.Wxuserdata.AsNoTracking().ToList();
var UserInsert = wxGetUser.userlist?.Where(p =>
{
if (!cacheUsers.Any(s => s.Userid == p.userid)) return true;
return false;
}).ToList();
var UserUpdate = wxGetUser.userlist?.Where(n =>
{
if (cacheUsers.Any(s => s.Userid == n.userid && (s.Username != n.name || s.Slaves != n.alias))) return true;
return false;
}).ToList();
var UserDelete = cacheUsers.Where(p =>
{
if (!wxGetUser.userlist.Any(s => s.userid == p.Userid)) return true;
return false;
}).ToList();
if (!UserInsert.IsNullModels())
{
var users = new List<Wxuserdatum>();
foreach (var item in UserInsert)
{
var user = new Wxuserdatum()
{
Userid = item.userid,
Username = item.name,
Slaves = item.alias,
State = 1,
Creatime = DateTime.Now,
Changetime = DateTime.Now,
};
users.Add(user);
}
_mysql.Wxuserdata.AddRange(users);
_mysql.SaveChanges();
}
if (!UserUpdate.IsNullModels())
{
var users = new List<Wxuserdatum>();
foreach (var item in UserUpdate)
{
var user = cacheUsers.FirstOrDefault(n => n.Userid == item.userid);
if (!user.IsNullModel())
{
user.Username = item.name;
user.Slaves = item.alias;
user.Changetime = DateTime.Now;
users.Add(user);
}
}
_mysql.Wxuserdata.UpdateRange(users);
_mysql.SaveChanges();
}
if (!UserDelete.IsNullModels())
{
_mysql.Wxuserdata.RemoveRange(UserDelete);
_mysql.SaveChanges();
}
在Entity Framework Core中,即使在查询时使用了AsNoTracking()
方法来禁用追踪功能,但在更新实体时仍然可能会遇到"tracked"相关错误。这是因为在更新实体时,EF Core仍然会尝试将实体对象附加到上下文中进行追踪,以便进行更改跟踪和保存。
Update()
方法替代UpdateRange()
方法:Update()
方法只更新单个实体对象,而不会涉及到多个实体对象的批量更新。因此,使用Update()
方法可能不会引发"tracked"相关错误。例如:context.Update(entity);
Attach()
方法显式附加实体对象:在使用UpdateRange()
方法之前,先使用Attach()
方法显式地将实体对象附加到上下文中,这样EF Core就不会尝试追踪已存在的实体对象。例如:foreach (var entity in entities)
{
context.Attach(entity);
}
context.UpdateRange(entities);
Detach()
方法将实体对象从上下文中分离:使用Detach()
方法可以将实体对象从上下文中分离,这样EF Core就不会再追踪该实体对象。例如:foreach (var entity in entities)
{
context.Entry(entity).State = EntityState.Detached;
}
context.UpdateRange(entities);
这三种方式试了还是都不行0.0
@笑叹、: 如果在使用上述方法后仍然遇到"tracked"相关错误,可能是因为实体对象的状态仍然被EF Core跟踪。在这种情况下,你可以尝试使用以下方法来解决问题:
Entry()
方法将实体对象的状态设置为Detached
:foreach (var entity in entities)
{
context.Entry(entity).State = EntityState.Detached;
}
context.UpdateRange(entities);
DbContext
的ChangeTracker
属性来取消跟踪实体对象:foreach (var entity in entities)
{
context.ChangeTracker.Clear();
context.Update(entity);
}
DbContext
的Entry()
方法将实体对象的状态设置为Detached
:foreach (var entity in entities)
{
context.Entry(entity).State = EntityState.Detached;
context.Update(entity);
}
请注意,以上方法中的entities
是一个实体对象的集合,你需要根据你的实际情况将其替换为你要更新的实体对象集合。此外,你还需要确保在更新实体对象之前,实体对象的状态已经被设置为Detached
,以避免"tracked"相关错误。
如果以上方法仍然无法解决问题,可能需要进一步检查你的代码逻辑和数据访问层的实现,以确定是否存在其他原因导致"tracked"相关错误的发生。
@lanedm: 跟踪实体状态都是Detached 1、3还是报错 2执行一条后 后面的更新都没实际生效
@笑叹、: 1.使用AsNoTracking()方法之前,先调用AsNoTracking()方法,这样可以让EF Core在查询时不再对结果进行追踪。例如:
var query = context.Set<TEntity>().AsNoTracking().Select(x => x);
2.在使用UpdateRange()方法之前,先调用Change跟踪关闭方法,例如:
context.ChangeTracker.QueryCacheProfile = null;
3.在使用UpdateRange()方法之前,先清空EF Core的缓存,例如:
context.ChangeTracker.Entries().ToList().ForEach(x => x.State = EntityState.Detached);
@lanedm: 在foreach前加了 context.ChangeTracker.Clear(); 就可以了,感谢大佬
没搞懂AsNoTracking()为啥还是被跟踪了