oracle自定义对象类型必须先在数据库用create type创建object和table of类型,c#类需严格对齐字段名、顺序及类型,并实现ioraclecustomtype接口;还需注册udt映射、使用oracleudtcollection传参,且pl/sql端声明与接收方式须匹配。

Oracle自定义对象类型必须在数据库端提前创建
直接在C#里定义一个类,然后传给Oracle是不行的——Oracle根本不认识这个类型。你得先用 CREATE TYPE 在数据库里建好对应的 OBJECT 和 TABLE OF 类型,而且名称、字段名、字段顺序、数据类型(包括精度)必须和C#类严格对齐。
常见错误现象:ORA-04043: object XXX does not exist 或 ORA-00902: invalid datatype,基本都是因为数据库没建类型,或者类型名大小写不一致(Oracle默认大写,建议全大写声明)。
- 数据库端示例:
CREATE OR REPLACE TYPE PERSON_OBJ AS OBJECT (ID NUMBER, NAME VARCHAR2(50), BIRTH_DATE DATE);
CREATE OR REPLACE TYPE PERSON_ARRAY AS TABLE OF PERSON_OBJ;
- C#类字段名必须与
PERSON_OBJ完全一致(区分大小写),且需加[OracleObjectMapping]特性(若用 Oracle.ManagedDataAccess) - 注意:
NUMBER对应decimal或int(取决于精度),VARCHAR2对应string,DATE对应DateTime
使用 Oracle.ManagedDataAccess 时必须注册 UDT 映射
ODP.NET 的托管驱动(Oracle.ManagedDataAccess)不会自动识别你的 C# 类对应哪个 Oracle 类型,必须显式注册映射关系,否则会报 System.InvalidOperationException: Unable to cast object of type 'XXX' to type 'Oracle.ManagedDataAccess.Types.IOracleCustomType'。
注册必须在应用启动时一次性完成,不能每次调用前重复注册(会抛异常)。
- 在
Program.cs或Startup.cs中调用:OracleUdtMappingAttribute.AddMapping(typeof(Person), "SCHEMA.PERSON_OBJ");
- 注意命名空间格式:必须是
schema_name.type_name,如"HR.PERSON_OBJ";如果在当前用户下,可用"PERSON_OBJ",但显式带 schema 更稳妥 - 你的 C# 类必须实现
IOracleCustomType接口,并提供FromCustomObject/ToCustomObject方法,用于字段与 Oracle 结构体之间的双向转换
构造数组参数时要用 OracleUdtCollection,不是普通 List 或 array
即使你把类映射好了,也不能直接把 List<person></person> 或 Person[] 赋给 OracleParameter.Value。Oracle 驱动只认它自己的集合包装类型 —— OracleUdtCollection。
错误做法:param.Value = persons.ToArray(); → 报 Invalid cast 或静默失败(参数为空)。
- 正确构造方式:
var collection = new OracleUdtCollection(typeof(Person), persons.ToArray());
param.OracleDbType = OracleDbType.Object;
param.UdtTypeName = "HR.PERSON_ARRAY"; // 注意是 ARRAY 类型名,不是 OBJECT
param.Value = collection;
-
UdtTypeName必须指向你在数据库中创建的TABLE OF类型(如PERSON_ARRAY),不是单个PERSON_OBJ - 确保
persons非空;空集合可传new OracleUdtCollection(...),但底层仍需初始化容量,建议至少传一个 dummy 元素再清空,或查文档确认空集合支持情况
存储过程参数方向与 PL/SQL 端接收方式要匹配
即使 C# 端全对了,PL/SQL 存储过程中如果把参数声明成 IN OUT 却没做任何赋值,或者用了错误的循环方式遍历,也会导致数据“消失”或报 ORA-06550。
典型错误:在存储过程中用 FOR i IN 1..p_array.COUNT LOOP,但传入的是空集合,COUNT 为 0,循环不执行,看起来像没收到数据。
- PL/SQL 参数声明示例:
PROCEDURE process_persons(p_data IN PERSON_ARRAY)
- 遍历时建议用
FORALL批量插入,或用EXTEND+LAST辅助判断边界 - 调试技巧:在存储过程中加
DBMS_OUTPUT.PUT_LINE(p_data.COUNT);,确认是否真传进来了 - 注意字符集:若
NAME字段含中文,确保数据库字符集(如 AL32UTF8)与 .NET 端编码一致,否则可能乱码或截断
最易被忽略的一点:UDT 映射注册是一次性全局行为,一旦漏掉或放错位置(比如写在某个方法内部),整个应用就无法传递该类型;而 Oracle 端类型名称大小写、schema 前缀、ARRAY vs OBJECT 的混淆,几乎占了这类问题的 80%。











