You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 07:52:52