java可通过sqlexception.geterrorcode()直接获取ora-编号(如20001),而.net中oracleexception.number返回的是内部错误号(如6550),必须用正则从message提取ora-(\d{5})并校验范围20000–20999才可精准识别业务异常。

Java里能直接用getErrorCode()取ORA-编号,.NET不行
Oracle服务端用 RAISE_APPLICATION_ERROR(-20001, '余额不足') 抛出的错误,在.NET里不会自动映射成带业务语义的异常类型。JDBC驱动把错误码塞进 SQLException.getErrorCode(),而 .NET 的 Oracle.ManagedDataAccess(ODP.NET)虽然也抛 OracleException,但它的 Number 属性**不是**你传进去的 -20001 —— 它返回的是 Oracle 内部错误号(比如 6550、6512),跟业务逻辑无关。
真正承载业务错误码的字段是 OracleException.Source?不对。是 Message?也不可靠——可能被本地化或截断。正确路径只有一条:OracleException.Number 确实不可靠,但 OracleException.Data 里也没有你要的东西。必须转向 OracleException.ToString() 解析,或者更稳妥地:启用 ODP.NET 的扩展错误信息支持。
- 确认连接字符串含
ExtendedProperties=true(仅限 Oracle.ManagedDataAccess 3.21.10+) - 否则默认情况下,
OracleException.Number永远是 6550(PL/SQL: ORA-06550),完全丢失 -20001 - 开启后,
OracleException.Number仍不等于 -20001,但OracleException.AdditionalInformation字段会包含原始错误码(需手动解析字符串)
必须从 Message 字符串里提取 ORA-xxxxx,但不能用 String.Contains
很多人写 if (ex.Message.Contains("ORA-20001")),这是危险的:消息可能被 Oracle NLS_LANGUAGE 设置成中文,变成“ORA-20001: 余额不足”,也可能在集群环境下混入其他日志前缀,甚至因字符集问题出现乱码。
真正安全的做法是用正则匹配标准 ORA 格式,并取捕获组:
var match = Regex.Match(ex.Message, @"ORA-(\d{5})");
if (match.Success)
{
int oraCode = int.Parse(match.Groups[1].Value);
if (oraCode >= 20000 && oraCode
- 永远用
ORA-(\d{5}),不是ORA-\d+—— 避免匹配到 ORA-00600 这类内部错误 - 检查范围
20000–20999是硬性要求,这是 Oracle 官方为用户预留的错误号区间 - 不要依赖
ex.Source或ex.HResult,它们和 RAISE_APPLICATION_ERROR 无关
事务提交前就该捕获,别等 Commit() 才 try-catch
和 Java 一样,触发器里的 RAISE_APPLICATION_ERROR 是在 DML 执行时同步抛出的,不是延迟到事务提交阶段。如果你把 ExecuteNonQuery() 和 transaction.Commit() 分开写,又只在 Commit() 外面套 try-catch,那异常根本不会进你的 catch 块 —— 它早在 ExecuteNonQuery() 就崩了。
- 错误写法:
cmd.ExecuteNonQuery(); transaction.Commit();→ 只在Commit()外 catch - 正确写法:DML 操作和异常处理必须紧耦合,例如
try { cmd.ExecuteNonQuery(); } catch (OracleException ex) { ... } - 如果用了 Entity Framework Core,对应的是
context.SaveChanges()这一行抛异常,不是context.Database.CommitTransaction()
别用通用 Exception 捕获,也别吞掉 OracleException
写 catch (Exception ex) 看似保险,实际会让业务错误和连接超时、空引用混在一起,后续无法做精准重试或告警。反过来,写 catch (OracleException ex) { /* log and rethrow */ } 但里面只调 Console.WriteLine(ex),等于把关键错误码藏起来了。
- 必须单独 catch
OracleException,并在处理逻辑里解析ORA-xxxxx - 若需向上抛出语义化异常,应封装成自定义业务异常(如
InsufficientBalanceException),把oraCode和原始OracleException作为 InnerException 传入 - 绝对不要写
catch (OracleException) { }或catch (OracleException) { throw; }—— 前者静默丢错,后者没带上上下文就丢了堆栈
Oracle 的 RAISE_APPLICATION_ERROR 错误在 .NET 里没有“开箱即用”的语义映射,所有业务错误码都藏在 Message 字符串的固定格式里;解析它不是权宜之计,而是唯一可靠路径。











