Dapper原生不支持Oracle REF CURSOR,必须用DynamicParameters显式声明OracleDbType.RefCursor并设为Output方向,再通过Query()二次读取;存储过程OUT参数名须大小写严格匹配,且commandType必须为StoredProcedure。
Oracle查询结果映射失败:Dapper默认不支持REF CURSOR
dapper原生不识别oracle的ref cursor输出参数,直接用query执行带out sys_refcursor的存储过程会抛出oracle.manageddataaccess.client.oracleexception: ora-06550或空结果。这不是dapper配置问题,而是类型映射链断裂——oracle驱动返回的是oraclerefcursor对象,dapper根本没注册对应的解析器。
必须显式声明游标为OracleDbType.RefCursor,并用DynamicParameters传递,再通过Query二次读取:
var p = new DynamicParameters();
p.Add("result_cursor", dbType: OracleDbType.RefCursor, direction: ParameterDirection.Output);
var results = connection.Query<user>("GET_USERS", p, commandType: CommandType.StoredProcedure).ToList();
</user>
- 存储过程中
OUT参数名(如result_cursor)必须与PL/SQL中定义完全一致,大小写敏感 - 不能省略
commandType: CommandType.StoredProcedure,否则Dapper按文本SQL解析,无法触发游标绑定 - 确保连接字符串已启用
Unicode=true,否则中文字段可能乱码
日期字段映射异常:Oracle DATE vs TIMESTAMP精度丢失
Oracle的DATE类型实际包含秒级精度,但.NET的DateTime默认反序列化会截断毫秒位;更麻烦的是,若数据库列是TIMESTAMP(6),Dapper可能把微秒部分全置为0,导致WHERE created_time > :dt查不到刚插入的数据。
解决方案不是改C#类型,而是控制Oracle侧返回格式:
SELECT TO_CHAR(created_date, 'YYYY-MM-DD HH24:MI:SS') as created_date, id, name FROM users
- 避免在SELECT中直接写
created_date裸列,改用TO_CHAR或CAST(created_date AS DATE)统一精度 - 如果必须保留毫秒,实体类对应属性改用
DateTimeOffset,并在查询中用TO_TIMESTAMP_TZ显式转换 - 注意
Oracle.ManagedDataAccessv19.15+才完整支持TIMESTAMP WITH TIME ZONE到DateTimeOffset的自动映射
大批量查询性能骤降:未启用数组绑定导致N+1
用Query查1万行数据时,如果SQL里有IN子句且参数是List,Dapper默认展开成WHERE id IN (1,2,3,...,10000),Oracle硬解析压力爆炸,执行计划失效,响应时间从200ms跳到8s。
正确做法是让Oracle驱动接管批量参数:
var ids = new int[] { 1, 2, 3, 10000 };
var sql = "SELECT * FROM orders WHERE id = ANY (:ids)";
var results = connection.Query<order>(sql, new { ids }).ToList();
</order>
-
ANY (:ids)是Oracle原生数组绑定语法,Oracle.ManagedDataAccess会自动将int[]转为OracleCollectionType - 严禁对
List做string.Join拼接SQL,既不安全也绕过绑定优化 - 单次绑定元素上限受
OracleParameter.ArrayBindSize限制,默认1000,超限时需分批或调大该值
自定义类型映射被忽略:Oracle UDT需要手动注册
当Oracle数据库使用CREATE TYPE address_t AS OBJECT (...),且查询返回该UDT字段时,Dapper直接报Invalid cast from 'Oracle.ManagedDataAccess.Types.OracleObject' to 'Address'。它不会自动触发TypeHandler,因为底层驱动返回的是封装后的OracleObject,不是原始字段值。
必须在应用启动时注册ODP.NET的类型映射:
OracleConfiguration.TypeMappings.Add(
new OracleTypeMapping("SCHEMA.ADDRESS_T", typeof(Address))
);
-
"SCHEMA.ADDRESS_T"中的SCHEMA必须全大写,且与数据库中USER_TYPES视图显示的OWNER一致 -
Address类需有无参构造函数,并用[OracleObjectMapping]标记属性与UDT字段的对应关系 - 此注册必须在首次创建
OracleConnection前完成,放错位置会导致静默失效
UDT映射是整个链路里最易被跳过的环节——连错误信息都指向Dapper,但根因在ODP.NET初始化时机和大小写匹配规则。











