EF Core报错:无法访问已释放对象,但变更已保存,原因何在?
问题分析与解决方案
这个问题的核心原因很明确——你没有等待异步方法执行完成,导致Scoped生命周期的DbContext被提前释放,而此时异步操作还在后台运行。
为什么会出现这个情况?
- DbContext的生命周期:你通过
services.AddDbContext<DataContext>(...)注册的DbContext默认是Scoped生命周期,也就是每个HTTP请求会创建一个实例,请求处理完成后,依赖注入容器会自动调用它的Dispose()方法释放资源。 - 异步调用未等待:在你的调用流程中,执行
_service.UpdateTagAsync(tagUpdateDto);时没有加await关键字。这意味着这个异步方法会立刻返回,当前请求的处理流程会继续执行直至结束,DI容器随即释放了DataContext实例。 - 滞后的异常:虽然数据库的修改操作(由
SaveChangesAsync发起)已经成功完成并提交到数据库,但SaveChangesAsync完成后还有一些后续的上下文操作(比如更新实体跟踪状态),此时它尝试访问已经被Dispose的DataContext,就会抛出你看到的异常。
怎么解决?
最直接的修复就是在调用异步方法时加上await,确保异步操作完成后再继续后续流程,避免DbContext被提前释放:
// 加上await,等待异步方法执行完成 await _service.UpdateTagAsync(tagUpdateDto);
同时,要确保调用这个方法的上层方法(比如Controller的Action)也标记为async,并返回Task或Task<T>类型:
public async Task<IActionResult> UpdateTag(int id, [FromBody] JsonPatchDocument<TagUpdateAPI> jsonPatchDocument) { TagDisplayDto tagDto = _service.GetTag(id); TagUpdateAPI tagForUpdate = _mapper.Map<TagUpdateAPI>(tagDto); jsonPatchDocument.ApplyTo(tagForUpdate); TagUpdateDto tagUpdateDto = _mapper.Map<TagUpdateDto>(tagForUpdate); // 必须等待异步方法完成 await _service.UpdateTagAsync(tagUpdateDto); return Ok(); }
额外的优化建议
为了让整个异步流程更规范,建议把服务里的同步查询也换成异步版本:
// 将同步的FirstOrDefault()换成异步的FirstOrDefaultAsync() public async Task UpdateTagAsync(TagUpdateDto tagDto) { var tag = await _dbContext.Tags.Where(p => p.Id == tagDto.Id).FirstOrDefaultAsync(); tag = _mapper.Map<TagUpdateDto, Tag>(tagDto, tag); tag.UpdatedAt = DateTime.Now; _dbContext.Tags.Update(tag); await _dbContext.SaveChangesAsync(); }
这样可以避免异步流程中出现同步阻塞,让代码更符合异步编程的最佳实践。
内容的提问来源于stack exchange,提问作者Oleg Sh
相关产品推荐
相关产品推荐

