oracle ref cursor 在 .net 中必须用 oracledbtype.refcursor 显式声明,direction 设为 output,通过 executenonquery 获取 oraclerefcursor 后调用 getdatareader 读取;不可直接用 fill 或 executereaderasync。
oracle ref cursor 在 .net 中必须用 oracledbtype.refcursor
直接把 ref cursor 当成普通输出参数会报错,比如 ora-06550 或 invalid operation for this connection type。.net 的 oracle data provider(odp.net)要求显式声明 ref cursor 类型,否则驱动无法识别游标结构。
关键点:
-
OracleParameter的OracleDbType必须设为OracleDbType.RefCursor,不能是Object、Xml或留空 -
Direction必须是ParameterDirection.Output(存储过程)或ParameterDirection.ReturnValue(函数返回 REF CURSOR,但极少用) - 连接必须使用
OracleConnection,且在打开状态下执行命令;OracleCommand的BindByName建议设为true,避免参数顺序错乱
调用存储过程时,REF CURSOR 要作为 OUTPUT 参数传入
Oracle 存储过程中通常将 REF CURSOR 定义为 OUT 参数,例如:PROCEDURE get_employees(p_dept_id IN NUMBER, p_cursor OUT SYS_REFCURSOR)。.NET 端需严格匹配该签名。
典型写法示例:
var cmd = new OracleCommand("get_employees", conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.BindByName = true;
<p>cmd.Parameters.Add(new OracleParameter("p_dept_id", OracleDbType.Int32) { Value = 10 });
var cursorParam = new OracleParameter("p_cursor", OracleDbType.RefCursor) { Direction = ParameterDirection.Output };
cmd.Parameters.Add(cursorParam);</p><p>conn.Open();
cmd.ExecuteNonQuery(); // 注意:这里用 ExecuteNonQuery,不是 ExecuteReader</p><p>// 游标结果在 cursorParam.Value 中,需转为 OracleRefCursor
var refCursor = (OracleRefCursor)cursorParam.Value;
using (var reader = refCursor.GetDataReader())
{
while (reader.Read())
{
Console.WriteLine(reader["EMP_NAME"]);
}
}</p>
注意:ExecuteNonQuery() 是必须的——因为 REF CURSOR 是输出参数,不是命令直接返回的结果集。
别在 OracleDataAdapter.Fill() 里直接传 REF CURSOR 参数
常见错误是试图这样写:adapter.SelectCommand = cmd; adapter.Fill(dt),但 Fill() 会忽略已设置的 REF CURSOR 输出参数,导致 dt.Rows.Count == 0 或抛出 InvalidCastException。
正确路径只有两条:
- 先
ExecuteNonQuery()获取OracleRefCursor,再用GetDataReader()+ 手动读取或DataTable.Load() - 或者用
OracleRefCursor.GetDataSet()(仅 ODP.NET Managed Driver v19.10+ 支持),但该方法内部仍走GetDataReader(),不推荐用于大数据量场景
若坚持用 DataTable,推荐写法:
var dt = new DataTable();
using (var reader = ((OracleRefCursor)cursorParam.Value).GetDataReader())
{
dt.Load(reader); // 安全、兼容性好、自动推断列类型
}
异步调用时 REF CURSOR 不支持 ExecuteReaderAsync
ODP.NET 的异步 API 对 REF CURSOR 支持有限:ExecuteReaderAsync() 不能用于带 REF CURSOR 输出参数的命令,否则会抛 NotSupportedException 或静默失败。
替代方案只有:
- 用
Task.Run(() => { /* 同步 ExecuteNonQuery + GetDataReader */ })包装(适用于 I/O 密集但非高并发场景) - 改用 Oracle 的隐式结果集(Oracle 12c+,用
SELECT ...直接返回,配合OracleCommand.FetchSize控制内存) - 升级到 ODP.NET Core / ODP.NET Managed Driver v21.x 并确认启用
EnableImplicitResults=true连接字符串选项(此时可绕过 REF CURSOR,改用标准ExecuteReaderAsync)
隐式结果集虽方便,但要求 Oracle 端重写存储过程,且老版本数据库不支持——这点容易被开发初期忽略。











