ASP.NET MVC/API中DELETE方法返回异常响应问题排查与优化咨询
咱们先直接揪出导致你始终返回400的核心问题:你的Id转换逻辑完全写错了,导致ids列表永远是空的!
看这段出问题的代码:
var ids = (await _testTableTbRepository.GetAllAsync()) .Select(_ => new TestTableTbModel { Id = _.Id }).OfType<int>().ToList();
你先把数据库里的每条记录都映射成了TestTableTbModel对象,然后调用OfType<int>——但TestTableTbModel是个自定义的模型类,根本不是int类型啊!这个操作会把所有元素都过滤掉,最终ids就是个空列表。不管你的model.Id是什么值,!ids.Contains(model.Id)肯定是true,自然每次都返回400错误。
先修复当前逻辑
最简单的修复就是直接提取Id的整数数值,没必要创建TestTableTbModel对象:
var ids = (await _testTableTbRepository.GetAllAsync()) .Select(_ => _.Id) // 直接取每条记录的Id值,类型就是int .ToList();
这样ids就会是数据库中所有Id的整数列表,就能正确判断目标Id是否存在了。
更优的实现方式(强烈推荐)
不过上面的修复只是解决了bug,但性能很差——每次都要从数据库拉取所有记录的Id,数据量大的时候会拖慢接口响应。更好的做法是直接在数据库层面检查目标Id是否存在,不用拉取全部数据:
方式1:给仓储加个ExistsAsync方法
让你的_testTableTbRepository新增一个ExistsAsync(int id)方法,内部只做一个轻量的存在性查询(比如EF Core里用AnyAsync):
// 仓储层实现示例(EF Core) public async Task<bool> ExistsAsync(int id) { return await _dbContext.TestTableTb.AnyAsync(x => x.Id == id); }
然后接口里就可以这样用:
try { var isIdExists = await _testTableTbRepository.ExistsAsync(model.Id); if (!isIdExists) { return StatusCode(400, $"Id: {model.Id} does not exist"); } await _testTableTbRepository.DeleteAsync(model.Id); return Ok(model.Id); } catch (Exception e) { return StatusCode(500, e); }
这种方式只需要一次极小的数据库查询,性能比拉所有Id好太多。
方式2:利用DeleteAsync的返回值
有些仓储的DeleteAsync方法会返回受影响的行数(比如EF Core中SaveChangesAsync会返回修改的行数),你可以直接检查这个返回值:如果是0,说明目标Id不存在,没有记录被删除:
try { var affectedRows = await _testTableTbRepository.DeleteAsync(model.Id); if (affectedRows == 0) { return StatusCode(400, $"Id: {model.Id} does not exist"); } return Ok(model.Id); } catch (Exception e) { return StatusCode(500, e); }
这种方式连额外的存在性查询都省了,效率最高。
最后:把重复逻辑抽成公共方法
既然你说DELETE和PUT有相同的检查逻辑,那最好把这个Id验证逻辑抽成一个私有方法,避免代码重复:
private async Task<IActionResult> CheckIdExists(int id) { var exists = await _testTableTbRepository.ExistsAsync(id); if (!exists) { return StatusCode(400, $"Id: {id} does not exist"); } return null; // 验证通过就返回null,继续执行后续逻辑 }
然后在DELETE和PUT方法里调用:
// DELETE方法示例 try { var validationResult = await CheckIdExists(model.Id); if (validationResult != null) { return validationResult; } await _testTableTbRepository.DeleteAsync(model.Id); return Ok(model.Id); } catch (Exception e) { return StatusCode(500, e); }
这样既解决了当前的bug,又优化了性能,还让代码更整洁。
内容的提问来源于stack exchange,提问作者tb517

