.NET 7+MediatR环境下无法捕获存储过程抛出的SqlException
问题分析与解决方案
核心问题
你当前通过判断Exception.Source来识别SQL错误,但这种方式无法将Exception强制转换为SqlException,自然访问不到SqlException特有的属性(比如Number、Errors集合)。同时需要确认存储过程的错误抛出逻辑是否符合SQL Server驱动的异常触发条件。
解决方案步骤
1. 直接捕获SqlException
修改C#的catch块,优先捕获SqlException,这样就能直接访问它的专属属性:
public async Task<BaseResponse<CivilStatusResponseDto>> Handle(CreateCivilStatusCommand request, CancellationToken cancellationToken) { BaseResponse<CivilStatusResponseDto> response = new BaseResponse<CivilStatusResponseDto>(); try { CivilStatus civilStatus = await _civilStatusRepository.SaveCivilStatus(_mapper.Map<CivilStatus>(request)); response.IsSuccess = true; response.Data = _mapper.Map<CivilStatusResponseDto>(civilStatus); response.Message = GlobalMessages.MESSAGE_SAVE; } catch (SqlException sqlEx) { // 直接访问SqlException的专属属性 response.IsSuccess = false; response.Message = sqlEx.Message; // 示例:获取错误编号、遍历错误集合 // int errorNumber = sqlEx.Number; // foreach (SqlError error in sqlEx.Errors) // { // WatchLogger.Log(error.Message); // } } catch (Exception ex) { response.IsSuccess = false; response.Message = GlobalMessages.MESSAGE_EXCEPTION; WatchLogger.Log(ex.Message); } return response; }
注意:代码需引用
Microsoft.Data.SqlClient(当前推荐)或System.Data.SqlClient(已弃用)命名空间,否则无法识别SqlException。
2. 优化存储过程的错误抛出逻辑
你的存储过程中初始RAISERROR使用严重级别12,而CATCH块重新抛出级别16。SQL Server驱动仅会将严重级别≥16的错误视为可捕获异常,建议统一使用级别16,或改用THROW(SQL Server 2012+支持)保留原始错误信息:
-- 替换原RAISERROR为THROW(自定义错误号需在50000-2147483647之间) IF EXISTS (SELECT 1 FROM CivilStates AS cs WHERE cs.CivilStatusId = @CivilStatusId) BEGIN THROW 50001, 'Ya se encuentra un registro en el sistema, con este codigo', 1; END IF EXISTS (SELECT 1 FROM CivilStates AS cs WHERE UPPER(cs.CivilStatusName) = UPPER(@CivilStatusName)) BEGIN THROW 50002, 'Ya se encuentra un registro en el sistema, con este estado civil', 1; END
THROW无需手动指定严重级别,默认会以级别16抛出,且保留错误上下文,更便于调试。
3. 处理可能的异常包装
如果SaveCivilStatus内部使用Task但未正确处理异常,可能会抛出AggregateException,此时需通过InnerException获取真实的SqlException:
catch (AggregateException aggEx) { if (aggEx.InnerException is SqlException sqlEx) { response.IsSuccess = false; response.Message = sqlEx.Message; } else { response.IsSuccess = false; response.Message = GlobalMessages.MESSAGE_EXCEPTION; WatchLogger.Log(aggEx.ToString()); } }
验证要点
- 确认使用的数据库驱动版本(优先
Microsoft.Data.SqlClient) - 测试存储过程的验证逻辑,确保错误能正确抛出并被C#捕获
- 调试时查看异常完整调用栈,确认是否存在异常被多层包装的情况
内容的提问来源于stack exchange,提问作者Ing. Jose Valera
相关产品推荐
相关产品推荐

