C#中SqlCommand ExecuteReader无法读取CATCH语句返回行的问题
问题原因分析
当INSERT触发主键/外键约束冲突时,SQL Server会返回两个结果集:
- 第一个是来自INSERT语句中
OUTPUT子句的空结果集(因为INSERT执行失败,没有数据输出) - 第二个才是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
相关产品推荐
相关产品推荐

