Oracle存储过程需用OUT参数绑定SYS_REFCURSOR类型返回结果集,C#中须显式指定OracleDbType.RefCursor及ParameterDirection.Output,否则报类型错误或空结果。
oracle 存储过程不能直接返回结果集,但可以通过 out 参数绑定 ref cursor 类型,让 c# 的 oracle data provider(odp.net)接收并消费游标。这是最标准、最稳定的跨语言结果集传递方式。
存储过程中必须声明 REF CURSOR 类型的 OUT 参数
Oracle 端不能用 RETURN 返回结果集,必须显式定义一个 OUT 参数,类型为 SYS_REFCURSOR 或自定义弱类型游标。强类型游标(如基于 %ROWTYPE)在 ODP.NET 中不被直接支持,会报 ORA-06550 或类型不匹配错误。
常见写法:
CREATE OR REPLACE PROCEDURE get_employee_list(
p_dept_id IN NUMBER,
p_cursor OUT SYS_REFCURSOR
) AS
BEGIN
OPEN p_cursor FOR
SELECT employee_id, first_name, salary FROM employees
WHERE department_id = p_dept_id;
END;-
p_cursor必须是OUT(不是IN OUT),且类型严格为SYS_REFCURSOR - 不要在存储过程中对游标做
CLOSE—— ODP.NET 会在OracleDataReader关闭时自动释放 - 若用自定义游标类型(如
TYPE emp_cursor IS REF CURSOR),C# 端仍需统一映射为OracleDbType.RefCursor,无需额外声明类型
C# 中必须用 OracleParameter 指定 OracleDbType.RefCursor
ODP.NET 不会自动推断游标参数类型,OracleParameter 的 OracleDbType 属性必须显式设为 OracleDbType.RefCursor,否则会抛出 System.InvalidCastException 或空结果集。
关键代码片段:
using (var conn = new OracleConnection(connStr))
{
conn.Open();
using (var cmd = conn.CreateCommand())
{
cmd.CommandText = "get_employee_list";
cmd.CommandType = CommandType.StoredProcedure;
<pre class='brush:php;toolbar:false;'> cmd.Parameters.Add("p_dept_id", OracleDbType.Int32).Value = 10;
var cursorParam = cmd.Parameters.Add("p_cursor", OracleDbType.RefCursor);
cursorParam.Direction = ParameterDirection.Output; // 必须设为 Output
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
Console.WriteLine($"{reader["employee_id"]}, {reader["first_name"]}");
}
}
}}
-
Direction必须是ParameterDirection.Output,设成InputOutput会导致执行失败 - 不要给
RefCursor参数赋值(.Value = ...),它只用于接收结果 - 如果存储过程有多个
REF CURSOR输出参数,每个都得单独声明为OracleDbType.RefCursor,且ExecuteReader()只能读第一个;多结果集要用ExecuteNonQuery()+ 多次GetRefCursor()(ODP.NET 21c+ 支持)
常见错误:ORA-06550 / “无效的游标” / 空结果集
这类错误基本都源于类型或方向错配,而不是 SQL 本身问题。尤其注意以下三点:
- 存储过程里把
p_cursor写成IN或漏写OUT—— C# 调用时参数方向必须严格对应 - C# 中用了
cmd.Parameters.AddWithValue(...)—— 它无法推断RefCursor,必须用带类型构造的Add()重载 - 连接字符串未启用
Unicode=True(非必需),但若字段含中文且 NLS 设置不一致,可能造成乱码而非报错;真正影响游标的是RefCursor类型声明和方向 - Oracle 用户权限不足(缺少
EXECUTEon procedure 或SELECTon tables)会导致ORA-00942,看起来像游标失败,实则权限问题
REF CURSOR 是 Oracle 和 .NET 之间传递动态结果集的事实标准,但它本质上是个服务器端资源句柄,生命周期完全由 Oracle 控制。C# 端唯一要盯死的,就是参数类型、方向、以及别提前关闭连接或命令对象 —— 游标在 OracleDataReader 读完前必须保持连接有效。



















