我有以下代码,我从前端收到不完整的实体,进行更改并更新它。之后,我从数据库中检索现已完成的相同实体,进行新的更改并再次更新...... (为了更好地理解,业务规则已被删除。)
`
public async Task<SeparationRequest> CollectSignature(SeparationRequest separationRequest)
{
await base.UpdateAsync(separationRequest);
int IdSeparationRequestAux = separationRequest.Id;
separationRequest = null;
var newSeparationRequest = await GetByIdAsync(IdSeparationRequestAux);
await base.UpdateAsync(newSeparationRequest );
return newSeparationRequest;
}
`
这是 UpdateAsync 方法: `
public virtual async Task<TEntity> UpdateAsync(TEntity entity)
{
Repository.Update(entity);
await _unitOfWork.SaveChangesAsync();
return entity;
}
`
这是 GetByIdASync 方法: `
public override async Task<SeparationRequest> GetByIdAsync(object id)
{
var apps = await Repository.GetFirstOrDefaultAsync(predicate: x => x.Id == (int)id, disableTracking: true,
include: x => x.AsNoTracking().Include(x => x.SeparationRequestStatus)
.Include(x => x.Itens)
.ThenInclude(x => x.SeparationRequestItemStatus));
return apps;
}
`
GetByIdAsync 方法调用以下方法:Get FirstOrDefaultAsync `
public virtual async Task<TEntity> GetFirstOrDefaultAsync(Expression<Func<TEntity, bool>> predicate = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> include = null,
bool disableTracking = true,
bool ignoreQueryFilters = false)
{
IQueryable<TEntity> query = _dbSet;
if (disableTracking)
{
query = query.AsNoTracking();
}
if (include != null)
{
query = include(query);
}
if (predicate != null)
{
query = query.Where(predicate);
}
if (ignoreQueryFilters)
{
query = query.IgnoreQueryFilters();
}
if (orderBy != null)
{
return await orderBy(query).FirstOrDefaultAsync();
}
else
{
return await query.FirstOrDefaultAsync();
}
}
`
第一个 updateAsync 已成功执行,但是当点击第二个 UpdateAsync 时,会触发以下异常:
System.InvalidOperationException:“无法跟踪实体类型“SeparationRequest”的实例,因为已跟踪具有相同键值 {'Id'} 的另一个实例。附加现有实体时,请确保仅附加一个具有给定键值的实体实例。考虑使用“DbContextOptionsBuilder.EnableSensitiveDataLogging”来查看冲突的键值。”
有人知道我能做什么吗? obs:我正在使用 AddScopped 和 AddTransient 我得到了同样的错误
由于您的 DBContext 被多个请求共享,这意味着您正在编辑的实体已被跟踪。
这可能是因为您的存储库服务是单例而不是作用域,因此您的数据库上下文在拉出时与正在跟踪的实体一起重用,然后放回到数据库上下文的同一实例中。
使用 Scoped Repository 代替 Singleton 存储库,这意味着将为每个请求创建一个新实例。同样,您还将拥有每个请求的数据库上下文。
当您注册服务时,它将使用 services.AddSingleton<..>
将其更改为 services.AddScoped<..>,当您注入它时,您将为每个请求获得一个新实例,并且您的更新应该可以正常工作。
参考:
以下答案可能不适用于此问题,但也可能适用,并且可能会帮助面临类似问题的其他人:
“双击”可能会出现这样的错误。 您的 Api 连续两次收到相同的请求;就像循环中可能发生的事情一样。
第一个创建并可能保存身份验证令牌,第二个具有数据库提供的完全相同的实体。 如果此调用是发布或删除,则第一个调用会顺利完成,但第二个调用将进行更新,并且实体框架会抛出异常,因为实体不再处于与数据库相同的状态。
您可以通过打开邮递员调用并在调试模式下对 api 进行两次背靠背的发布/删除调用(并在控制器上设置断点)来看到这种情况的发生。 即使 API 可能具有范围实体框架,错误仍然会发生,因为第二次调用时实体的状态会有所不同。