Dapper调用Oracle存储过程需显式指定OracleDbType并使用全限定名,REF CURSOR须用Dapper.Oracle包的OracleDynamicParameters,纯执行用Execute,返回结果集用Query或QueryMultiple。
用Dapper调用Oracle存储过程时,DynamicParameters必须显式指定OracleDbType
直接用new { id = 1 }传参会失败——dapper默认按sql server语义推断类型,oracle不认int或string对应的底层类型。比如传number字段,若不声明oracledbtype.int32,odp.net可能把它当varchar2处理,导致ora-06502错误。
正确做法是始终用DynamicParameters并明确指定OracleDbType:
var p = new DynamicParameters();
p.Add("p_order_id", orderId, OracleDbType.Int32, ParameterDirection.Input);
p.Add("p_user_id", userId, OracleDbType.Varchar2, ParameterDirection.Input);
p.Add("p_result_msg", dbType: OracleDbType.Varchar2, size: 200, direction: ParameterDirection.Output);
-
size对Varchar2/NVarchar2必须设,否则输出参数可能截断或报ORA-06502 - 输入参数也建议带
OracleDbType,避免隐式转换引发精度丢失(如decimal转NUMBER(10,2)) - 别用
@param前缀——Oracle存储过程参数名不带@,写"p_order_id"就行
遇到REF CURSOR输出参数就别硬刚DynamicParameters
DynamicParameters原生不支持OracleDbType.RefCursor,强行加会抛异常:“无法将OracleRefCursor转换为DataTable或IEnumerable”。这不是配置问题,是Dapper设计限制。
必须换方案:
- 用
Dapper.Oracle包(NuGet ID:Dapper.Oracle),它重写了OracleDynamicParameters,支持RefCursor和Get安全取值() - 安装后代码变成:
var p = new OracleDynamicParameters(); p.Add("p_cursor", dbType: OracleDbType.RefCursor, direction: ParameterDirection.Output); var results = connection.Query<orderdetail>("pkg_orders.get_order_items", p, commandType: CommandType.StoredProcedure);</orderdetail> - 别自己写
OracleDynamicParameters——GitHub上那个流行实现已包含自动类型映射,包括DATE转DateTime、NUMBER转decimal
存储过程名必须带Schema,不能只写过程名
常见错误是Query("get_user", ...)直接报ORA-00900或ORA-06576:不是有效函数或过程名。根本原因是Oracle要求全限定名,尤其跨Schema或使用同义词时。
确认方式:
- 连上数据库执行
SELECT owner, object_name FROM all_objects WHERE object_name = 'GET_USER' AND object_type = 'PROCEDURE'; - 如果返回
OWNER=HR,调用时就得写"HR.GET_USER",不能只写"GET_USER" - 连接字符串里
Data Source指向的是PDB还是CDB?如果是CDB,Schema名还得带上容器名,如C##HR.GET_USER
Execute和Query选错会导致数据丢或异常
存储过程有没有返回结果集,决定了该用哪个Dapper方法:
- 纯执行(INSERT/UPDATE/事务逻辑)→ 用
Execute(..., commandType: CommandType.StoredProcedure) - 有
REF CURSOR输出 → 必须用Query或() QueryMultiple(),否则游标被忽略 - 有
RETURN_VALUE(比如RETURN NUMBER)→ 用Execute+DynamicParameters.Get,但注意Oracle里这其实是("@return") OUT参数模拟的,不是SQL Server那种真正RETURN - 混用场景(比如先改数据再返列表)→ 用
QueryMultiple,它能顺序读多个结果集,比手动开多个Command省资源
最易忽略的一点:Oracle存储过程里DBMS_OUTPUT.PUT_LINE不会自动传回.NET,得在连接字符串加StmtCacheSize=0;Pooling=false;再配合OracleConnection.BeginTransaction()才能捕获——但这属于调试手段,上线别开。











