如何正确删除数据库记录?Web API遇DataReader异常求助
问题原因与解决方案
错误原因
这个异常是因为EF延迟加载导致的多活跃DataReader冲突:当你遍历_dataContext.Urls这类IQueryable对象时,数据库连接的DataReader会保持打开状态以逐行读取数据;此时在循环内部执行_dataContext.ShortedUrls.FirstOrDefault()这类新查询,同一个连接上会存在两个活跃的DataReader,触发InvalidOperationException。
修复方案
1. 先将数据加载到内存,关闭DataReader
遍历前用ToListAsync()把查询结果加载到本地内存,避免延迟加载导致的DataReader持续打开。
2. 修复普通用户分支的逻辑错误
原代码在foreach循环内直接return Ok(),会导致仅删除第一条记录就返回,需将保存和返回逻辑移到循环外。
3. 利用导航属性简化操作
Url实体已关联ShortUrl导航属性,无需再查询ShortedUrls表,直接通过item.ShortUrl获取对应短链接。
4. 优化查询条件
用外键UserId代替实体比较UserCreatedBy == user,生成的SQL更高效,避免实体跟踪问题。
修改后的完整代码
[HttpDelete] [Route("/DeleteUrls")] public async Task<IActionResult> DeleteUrls(string userName) { var user = await _dataContext.Users.FirstOrDefaultAsync(x => x.UserName == userName); if (user == null) { return BadRequest("No user with such Username"); } switch (user.IsAdmin) { case true: // 加载所有Url到内存,关闭DataReader var allUrls = await _dataContext.Urls.Include(u => u.ShortUrl).ToListAsync(); foreach (var url in allUrls) { // 利用导航属性直接获取ShortUrl if (url.ShortUrl != null) { _dataContext.ShortedUrls.Remove(url.ShortUrl); } _dataContext.Urls.Remove(url); } await _dataContext.SaveChangesAsync(); return Ok(); case false: // 用UserId查询,加载当前用户的所有Url及关联的ShortUrl var userUrls = await _dataContext.Urls .Include(u => u.ShortUrl) .Where(x => x.UserId == user.Id) .ToListAsync(); if (!userUrls.Any()) { return BadRequest("No urls found for this user"); } foreach (var url in userUrls) { if (url.ShortUrl != null) { _dataContext.ShortedUrls.Remove(url.ShortUrl); } _dataContext.Urls.Remove(url); } await _dataContext.SaveChangesAsync(); return Ok(); } }
可选补充:开启MARS(不推荐作为首选方案)
如果必须保持延迟加载,可以在数据库连接字符串中添加MultipleActiveResultSets=True,允许同一个连接上存在多个活跃的DataReader。但这种方式可能带来性能和资源占用问题,不如先加载到内存可控。
内容的提问来源于stack exchange,提问作者Hedimin
相关产品推荐
相关产品推荐

