更新同一个项目2次时出错

问题描述 投票:0回答:2

我有以下代码,我从前端收到不完整的实体,进行更改并更新它。之后,我从数据库中检索现已完成的相同实体,进行新的更改并再次更新...... (为了更好地理解,业务规则已被删除。)

`

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 我得到了同样的错误

c# entity-framework-core repository entity-framework-5 dbcontext
2个回答
0
投票

由于您的 DBContext 被多个请求共享,这意味着您正在编辑的实体已被跟踪。

这可能是因为您的存储库服务是单例而不是作用域,因此您的数据库上下文在拉出时与正在跟踪的实体一起重用,然后放回到数据库上下文的同一实例中。

使用 Scoped Repository 代替 Singleton 存储库,这意味着将为每个请求创建一个新实例。同样,您还将拥有每个请求的数据库上下文。

当您注册服务时,它将使用 services.AddSingleton<..>

将其更改为 services.AddScoped<..>,当您注入它时,您将为每个请求获得一个新实例,并且您的更新应该可以正常工作。

参考:

无法跟踪实体类型的实例,因为跟踪了具有相同键值的另一个实例


0
投票

以下答案可能不适用于此问题,但也可能适用,并且可能会帮助面临类似问题的其他人:

“双击”可能会出现这样的错误。 您的 Api 连续两次收到相同的请求;就像循环中可能发生的事情一样。

第一个创建并可能保存身份验证令牌,第二个具有数据库提供的完全相同的实体。 如果此调用是发布或删除,则第一个调用会顺利完成,但第二个调用将进行更新,并且实体框架会抛出异常,因为实体不再处于与数据库相同的状态。

您可以通过打开邮递员调用并在调试模式下对 api 进行两次背靠背的发布/删除调用(并在控制器上设置断点)来看到这种情况的发生。 即使 API 可能具有范围实体框架,错误仍然会发生,因为第二次调用时实体的状态会有所不同。

© www.soinside.com 2019 - 2024. All rights reserved.