必须用CommandType.StoredProcedure且参数方向、类型、名称须与PL/SQL端严格一致,否则易报ORA-06550或PLS-00306;ExecuteScalar()和ExecuteNonQuery()无法获取REF CURSOR结果,须用ExecuteReader()或OracleDataAdapter.Fill()。

必须用 OracleCommand.CommandType = CommandType.StoredProcedure,且所有参数方向、类型、名称必须与 PL/SQL 端严格一致;否则大概率报 ORA-06550 或 PLS-00306 错误。
为什么 ExecuteScalar() 和 ExecuteNonQuery() 会失败
Oracle 存储过程没有“返回值”概念,它靠 OUT 参数(包括 REF CURSOR)传出数据。ODP.NET 不会自动把 REF CURSOR 映射成 DataTable 或单个值:
-
ExecuteScalar()试图取第一个字段第一行,但游标未被打开或未绑定为输出参数 → 报Invalid operation. The connection is closed.或空引用 -
ExecuteNonQuery()只返回影响行数,对OUT参数无感知 → 读不到结果集,也拿不到OUT值 - 真正触发游标打开的是
ExecuteReader()或OracleDataAdapter.Fill()
OracleParameter 的 Direction 和 OracleDbType 必须显式指定
ODP.NET 不推断参数行为,哪怕存储过程签名明确,你也得手动告诉它每个参数是输入、输出、还是游标:
-
IN参数:设Direction = ParameterDirection.Input,OracleDbType按实际类型填(如OracleDbType.Varchar2) -
OUT参数(非游标):设Direction = ParameterDirection.Output,执行后从param.Value读值 -
REF CURSOR输出:必须用OracleDbType.RefCursor,且Direction = ParameterDirection.Output;不能省略cmd.Parameters.Add()这一步 - 参数名大小写敏感:Oracle 默认大写,C# 中传
"P_RESULT"而非"p_result",除非存储过程中用了双引号定义小写名
用 OracleDataAdapter.Fill() 比手写 ExecuteReader() 更稳
手动遍历 OracleDataReader 容易忽略边界情况,而 Fill() 内部做了容错封装:
- 自动处理游标未打开、字段名大小写不一致(比如 Oracle 返回
EMPLOYEE_ID,C# 按reader["employee_id"]访问会抛异常,DataTable则按列序索引更安全) - 支持一次填充多结果集(当存储过程
OPEN多个游标时) - 避免
reader.Read()循环漏掉Dispose或未检查HasRows - 示例片段:
using (var adapter = new OracleDataAdapter(cmd)) { var dt = new DataTable(); adapter.Fill(dt); // ← 自动调用 ExecuteReader 并映射 }
数组参数(PL/SQL Associative Array)要用 OracleCollectionType.PLSQLAssociativeArray
.NET 的 int[] 或 List<T> 不能直接传给 Oracle 表类型,必须走 ODP.NET 的集合绑定机制:
- Oracle 端需先定义
TYPE my_array_type IS TABLE OF NUMBER INDEX BY PLS_INTEGER; - C# 中创建参数时指定:
param.OracleCollectionType = OracleCollectionType.PLSQLAssociativeArray; -
param.Value赋值为一维数组(如new int[] { 100, 200, 300 }),不能是ArrayList或二维数组 - 别用已废弃的
System.Data.OracleClient,它根本不识别这种类型
最常被跳过的点是:没设 CommandType.StoredProcedure 就直接跑命令,或者把 REF CURSOR 当返回值接收——这两处一错,ORA-06550 几乎必现,而且错误堆栈不提示根本原因。


















