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

C#中SqlCommand ExecuteReader无法读取CATCH语句返回行的问题

问题原因分析

当INSERT触发主键/外键约束冲突时,SQL Server会返回两个结果集:

  1. 第一个是来自INSERT语句中OUTPUT子句的空结果集(因为INSERT执行失败,没有数据输出)
  2. 第二个才是CATCH块中SELECT语句返回的错误信息结果集

你的代码只调用了一次reader.Read(),读取的是第一个空结果集,自然会抛出“Invalid attempt to read when no data is present”异常。而SSMS会自动展示所有结果集,所以你能看到CATCH里的内容,但.NET的SqlDataReader默认只会处理第一个结果集。

解决方案

方案一:修改SQL语句,确保仅返回单个结果集

把OUTPUT子句替换为INSERT成功后的SELECT语句,这样无论成功还是失败,都只会返回一个结果集:

BEGIN TRY
    INSERT INTO Table1 (primkey, forkey) VALUES (@primkey, @forkey);
    SELECT -1 AS err, NULL as msg;
END TRY
BEGIN CATCH
    SELECT ERROR_NUMBER() AS err, ERROR_MESSAGE() as msg;
END CATCH;

修改后,你的原有.NET代码无需调整,reader.Read()就能直接读到正确的结果行。

方案二:在.NET代码中处理多结果集

如果不想修改SQL,可以通过遍历所有结果集,找到包含数据的那个再读取:

command.Parameters[0].Value = primKey;
command.Parameters[1].Value = forKey;
// 使用using自动释放reader,避免资源泄漏
using (SqlDataReader reader = command.ExecuteReader())
{
    do
    {
        if (reader.HasRows)
        {
            reader.Read();
            int errCode = reader.GetInt32(reader.GetOrdinal("err"));
            switch (errCode)
            {
                case -1: // 无错误
                case 2627: // 主键约束冲突,忽略
                    break;
                case 547: // 外键不存在
                    throw new DatabaseExceptions.RecordNotFoundException(this, forKey);
                default:
                    string errMsg = reader.GetString(reader.GetOrdinal("msg"));
                    throw new DatabaseExceptions.DBException(this, errMsg);
            }
            break;
        }
    } while (reader.NextResult());
}

这里通过reader.NextResult()切换结果集,用reader.HasRows判断当前结果集是否有数据,找到有效结果后再处理。

内容的提问来源于stack exchange,提问作者Enter Display Name Here

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 06:35:23