同一路由控制器中实现实体增删报DataReader已打开异常
问题描述
是否有方案可在同一路由控制器中同时实现实体的添加与删除操作?以下为实现点赞/取消点赞逻辑编写的C#接口,添加实体功能运行正常,但执行删除操作时抛出异常:
[HttpPost] public async Task<ActionResult<Upvote>> AddRemoveUpvote(CreateUpvoteDTO upvoteDtO) { if (_context.Upvotes == null) { return Problem("Entity set 'ApplicationDbContext.Upvotes' is null."); } var upvote = _mapper.Map<Upvote>(upvoteDtO); foreach (var item in _context.Upvotes) { if (item.UserId == upvote.UserId) { _context.Upvotes.Remove(item); await _context.SaveChangesAsync(); return Ok("Topic unliked"); } } _context.Upvotes.Add(upvote); await _context.SaveChangesAsync(); return Ok("Topic Liked"); }
触发的异常信息
System.InvalidOperationException: 已有与此连接关联的打开的DataReader,必须先将其关闭。
异常堆栈:Microsoft.Data.SqlClient.SqlInternalConnectionTds.ValidateConnectionForExecute(SqlCommand command)
异常堆栈:Microsoft.Data.SqlClient.SqlInternalConnection.BeginSqlTransaction(IsolationLevel iso, String transactionName, Boolean shouldReconnect)
错误原因
- 直接遍历
_context.Upvotes时,Entity Framework Core会使用DataReader以流式方式从数据库拉取数据,此时数据库连接被该DataReader独占,遍历未结束时调用SaveChangesAsync()会在同一个连接上发起新的数据库执行请求,直接触发连接占用异常。 - 原有逻辑存在业务漏洞:仅通过
UserId匹配点赞记录,未关联被点赞对象的唯一标识(如主题Id),会导致用户只要给任意内容点过赞,就会误删第一条匹配到的点赞记录,完全不符合点赞/取消点赞的业务预期。 - 全表遍历查询性能极差,当点赞表数据量增长到一定规模后,接口会出现严重的响应超时问题。
修复方案
- 放弃全表遍历写法,使用精准条件查询匹配的点赞记录,调用
FirstOrDefaultAsync()提前执行查询,待DataReader释放、连接回收后再执行增删操作,从根源上避免连接占用问题。 - 补全查询匹配条件,同时校验用户Id和被点赞对象Id,确保操作的是对应内容下的用户点赞记录。
- 简化逻辑分支:查询到已存在的点赞记录则执行删除,不存在则新增,操作完成后统一保存变更即可。
修复后的参考代码:
[HttpPost] public async Task<ActionResult> AddRemoveUpvote(CreateUpvoteDTO upvoteDto) { if (_context.Upvotes == null) { return Problem("Entity set 'ApplicationDbContext.Upvotes' is null."); } // 注意将TopicId替换为你实际DTO中对应的被点赞对象字段名 var existUpvote = await _context.Upvotes .FirstOrDefaultAsync(u => u.UserId == upvoteDto.UserId && u.TopicId == upvoteDto.TopicId); if (existUpvote != null) { _context.Upvotes.Remove(existUpvote); await _context.SaveChangesAsync(); return Ok("Topic unliked"); } var newUpvote = _mapper.Map<Upvote>(upvoteDto); _context.Upvotes.Add(newUpvote); await _context.SaveChangesAsync(); return Ok("Topic Liked"); }
内容的提问来源于stack exchange,提问作者john otor
相关产品推荐
相关产品推荐

