Dapper本身不直接支持Oracle REF CURSOR,必须用OracleParameter显式设Direction=Output、OracleDbType=RefCursor,并通过Query<T>(cmd => { cmd.ExecuteNonQuery(); return ((OracleCommand)cmd).ExecuteReader(); })接收结果。

Oracle 存储过程返回 REF CURSOR 怎么用 Dapper 接收
Dapper 本身不直接支持 Oracle 的 REF CURSOR 类型,必须通过 OracleCommand 显式设置参数方向和类型,并让 Dapper 处理后续结果集。关键不是“Dapper 能不能”,而是“怎么把游标转成 Dapper 认得的 IDataReader”。
- 存储过程中定义输出参数为
OUT SYS_REFCURSOR,比如:PROCEDURE get_users(p_result OUT SYS_REFCURSOR) - 调用时必须用
OracleParameter(来自Oracle.ManagedDataAccess),且Direction = ParameterDirection.Output,OracleDbType = OracleDbType.RefCursor - 不能用 Dapper 的常规
Query<T>()直接传参——它会把RefCursor当普通值绑定,导致 ORA-06550 或空结果
Dapper + Oracle.ManagedDataAccess 正确调用方式
核心是手动构造 OracleCommand,执行后把它的 ExecuteReader() 结果交给 Dapper 的 Query<T>() 扩展方法。Dapper 提供了 Query<T>(this IDbConnection, Func<IDbCommand, IDataReader>) 这个重载,就是为这种场景准备的。
var conn = new OracleConnection(connectionString);
conn.Open();
var cmd = conn.CreateCommand();
cmd.CommandText = "get_users";
cmd.CommandType = CommandType.StoredProcedure;
var refCursorParam = new OracleParameter("p_result", OracleDbType.RefCursor)
{
Direction = ParameterDirection.Output
};
cmd.Parameters.Add(refCursorParam);
// Dapper 不接管参数绑定,只消费 reader
var users = conn.Query<User>(cmd =>
{
cmd.ExecuteNonQuery(); // 先执行,触发游标填充
return ((OracleCommand)cmd).ExecuteReader(); // 返回 reader 给 Dapper
}).ToList();
- 必须用
ExecuteNonQuery()触发存储过程执行,否则ExecuteReader()会报 “command not executed” -
cmd参数实际是OracleCommand,所以要显式转换才能调ExecuteReader() - 别在
Query<T>()外部提前ExecuteReader()——Dapper 需要自己控制 reader 生命周期,否则可能抛InvalidOperation
常见错误:ORA-01008 / ORA-06550 / 空集合
这三个错误基本都指向同一个问题:Dapper 没拿到有效的结果集,或者游标没被正确打开。
-
ORA-01008: not all variables bound→ 忘记给所有 IN 参数赋值,或参数名大小写/前缀(如:p_namevsp_name)不匹配 Oracle 存储过程声明 -
ORA-06550: line X, column Y: PLS-00306: wrong number or types of arguments→ 参数数量、顺序、类型(尤其是RefCursor必须是Output)与存储过程签名不一致 - 返回空集合但无报错 → 存储过程中没真正
OPEN p_result FOR SELECT ...,或者 SQL 本身没数据;也可能是连接未开启(conn.State == ConnectionState.Closed)却直接调用命令
为什么不用 OracleDataAdapter 或 DataTable
可以,但没必要。Dapper 的设计目标就是轻量映射,而 DataTable 带来额外内存开销和类型转换成本。如果你已经用 Dapper 做其他查询,混用 DataTable 反而破坏一致性,还容易漏关 reader 或 connection。
- 用
Query<T>+ 手动OracleCommand是最贴近 Dapper 哲学的做法,类型安全、零中间对象、复用已有实体类 - 如果存储过程返回多个结果集(多个游标),Dapper 默认只读第一个;此时需用
QueryMultiple()配合多个RefCursor输出参数,每个都要单独设OracleDbType.RefCursor - 注意 Oracle 客户端版本兼容性:
Oracle.ManagedDataAccess 21.x对 .NET 6+ 更友好,老版本(如 19c)在 Linux 上可能有字符集或游标生命周期问题
真正麻烦的不是语法,而是 Oracle 游标依赖存储过程上下文——一旦连接断开、命令释放或异常中途退出,游标就失效。所以务必确保整个链路(connection → command → reader → Dapper mapping)都在同一 try/catch 里完成,且 connection 不被提前 dispose。


















