RAISERROR在C#中未被捕获,主因是severity级别错误(需11–19)、调用方式不当(如SqlDataAdapter.Fill()默认不抛异常)或缺少SET NOCOUNT ON导致消息混淆。

RAISERROR 不被 C# 捕获,大概率是 severity 级别设错了,或者调用方式绕过了异常传播路径。
severity 必须在 11–19 之间才能进 C# 的 SqlException
SQL Server 对 RAISERROR 的 severity 做了硬性分流:
-
severity <= 10:只输出到“消息”窗格,不中断执行,catch块完全静默 -
severity 11–19:触发异常队列,.NET 能收到SqlException,且可正常throw或处理 -
severity >= 20:属于致命错误,连接会被强制断开,C# 收到的往往不是原错误,而是类似"A transport-level error has occurred"这类底层异常
常见踩坑:写成 RAISERROR('库存不足', 10, 1) —— 看似报了错,其实客户端根本收不到异常。
ADO.NET 调用方式决定异常是否“透出”
不是所有执行方式都等价。以下操作中,只有部分能真正把 RAISERROR 当作异常抛给 C#:
-
SqlCommand.ExecuteNonQuery():✅ 正常捕获(推荐) -
SqlDataAdapter.Fill():⚠️ 默认不抛异常,除非你把SqlCommand设为UpdateCommand而非SelectCommand(曾有项目因此漏掉错误) -
SqlCommand.ExecuteReader():✅ 可捕获,但注意必须读取结果集前就触发异常,否则可能延迟暴露 -
Connection.Execute()(如 Dapper):✅ 多数版本支持,但需确认驱动版本兼容性
关键点:Fill() 类方法本质是“取数据”,它会吞掉非致命错误并继续返回空结果集;而 ExecuteNonQuery() 明确以“执行命令”为目标,异常路径更干净。
SET NOCOUNT ON 是隐性干扰项
存储过程开头没加 SET NOCOUNT ON,会导致 SQL Server 在每条语句后返回 “X 行受影响” 消息。这些消息和 RAISERROR 混在一起时,某些旧版 ADO.NET 驱动会误判最后一条“影响行数”为成功信号,从而跳过异常检查。
实操建议:
- 所有存储过程第一行加上
SET NOCOUNT ON - 确保
RAISERROR(..., 16, 1) WITH NOWAIT中的WITH NOWAIT存在——它让消息立即推送给客户端,避免缓冲延迟掩盖异常时机 - 不要依赖返回值(比如
RETURN -1)代替异常;异常才是语义清晰、不可绕过的失败信号
最易被忽略的其实是组合效应:低 severity + Fill() 调用 + 缺少 NOCOUNT,三者叠在一起时,错误就像蒸发了一样——日志里没记录,界面没提示,业务逻辑却悄悄走偏了。


















