必须用 OracleException.Number == 1 判断 ORA-00001,因其跨驱动、不被日志截断或语言环境干扰;嵌套异常中需递归提取 OracleException;再通过约束名查 user_constraints 定位重复字段;字符集不匹配也会误报该错误。

必须用 OracleException.Number 获取数字码,别解析 Message 字符串——这是唯一跨驱动、不被日志截断或语言环境干扰的稳定方式。
为什么不能用 Message.Contains("ORA-00001")
Oracle 客户端驱动(如 Oracle.ManagedDataAccess)在不同环境会动态改写错误消息:中文系统可能把 "ORA-00001" 翻译成“违反唯一约束条件”,日志中间件可能截断长消息、移除前缀,甚至把整个 ORA- 字样过滤掉。而 OracleException.Number 始终返回整数 1,与数据库服务端一致,且不受任何包装层影响。
-
OracleException.Number == 1是判断 ORA-00001 的唯一可靠依据 - 即使异常被
SqlException或AggregateException包裹多层,只要最内层是OracleException,Number就不变 - 不要依赖
SqlException.ErrorCode—— 它是 SQL Server 兼容码(如"23000"),无法区分 ORA-00001 和 ORA-02291
如何从嵌套异常中准确提取 OracleException
在 Entity Framework 或 Dapper 等框架中,原始 OracleException 常被包进 DbUpdateException、InvalidOperationException 甚至 AggregateException。直接 catch (OracleException ex) 会漏判。
- 写一个递归提取方法:
GetOracleException(Exception ex),循环调用ex.InnerException直到找到ex is OracleException - 注意:.NET 6+ 支持
Exception.GetBaseException(),但它不保证返回OracleException;仍需类型判断 - 示例片段:
private static OracleException GetOracleException(Exception ex)<br>{<br> while (ex != null)<br> {<br> if (ex is OracleException oracleEx) return oracleEx;<br> ex = ex.InnerException;<br> }<br> return null;<br>}
捕获后怎么告诉用户“哪个字段重复了”
拿到 Number == 1 后,不能只返回“数据已存在”。得查出具体约束名,再映射到业务字段。Oracle 不会在异常里直接告诉你“手机号重复”,只给 SYS_C0012321 这种系统名。
- 从
OracleException.Message中安全提取约束名:用正则匹配"unique constraint \(([^)]+)\)",取第一组(如"C##CJC.SYS_C008089") - 拆出约束名部分(去掉 schema 前缀),执行查询:
SELECT cols.column_name FROM user_constraints cons JOIN user_cons_columns cols ON cons.constraint_name = cols.constraint_name WHERE cons.constraint_name = 'SYS_C008089' - 注意:用
user_constraints而非all_constraints,权限低、速度快,开发/测试够用;生产环境若需跨 schema 查,再换all_constraints并加owner条件
容易被忽略的字符集陷阱:中文插入也报 ORA-00001?
当应用和数据库 NLS 字符集不一致(如客户端 NLS_LANG=AMERICAN_AMERICA.WE8ISO8859P1,但库中存的是 UTF8),中文字段可能全变成 ?。多个不同中文值插入后都变成 ???,被当成相同值触发 ORA-00001。
- 这不是代码逻辑问题,而是连接层配置缺陷
- 检查连接字符串是否显式指定
Unicode=True(Oracle.ManagedDataAccess默认为 true,但旧版System.Data.OracleClient默认 false) - 验证方式:插入一条含中文的记录,再用
SELECT DUMP(t_name) FROM t1查看实际字节值;若全是63(即?的 ASCII 码),就是字符集错配



















