Oracle UDT 与 Table Type 的数据库前置创建与验证 在 C# 中使用 ODP.NET 调用涉及 Oracle 自定义类型(UDT)或表类型(Table Type)的存储过程时,必须确保数据库端已预先创建并验证相关类型,同时在代码中正确配置参数类型、值和方向,并显式注册 UDT 映
在 C# 中使用 ODP.NET 调用涉及 Oracle 自定义类型(UDT)或表类型(Table Type)的存储过程时,必须确保数据库端已预先创建并验证相关类型,同时在代码中正确配置参数类型、值和方向,并显式注册 UDT 映射关系。任何环节的缺失都可能导致 ORA-04043、ORA-00902 等错误。
若数据库中的对象类型或表类型未正确创建,无论 C# 端的参数配置如何完善,系统都会抛出如 ORA-04043: object ... does not exist 或 ORA-00902: invalid datatype 的错误。问题的根源在于缺失必要的前提条件。
长期稳定更新的攒劲资源: >>>点此立即查看<<<

因此,在编写代码前,务必在数据库端确认以下两点:
OBJECT)已通过 CREATE OR REPLACE TYPE ... AS OBJECT 语句成功创建,且状态为 VALID。TABLE OF ...)已通过 CREATE OR REPLACE TYPE ... AS TABLE OF ... 语句创建,并且其所依赖的基础对象类型必须已存在。一个常见的错误场景是:先创建了 student_table_type,却遗漏了其依赖的 student_obj_type。这种情况下,程序可能在连接阶段不报错,但在执行环节会因类型解析失败而提示类型不存在。
需注意,已过时的 System.Data.OracleClient 不应再使用。必须使用 Oracle 官方的 ODP.NET(即 Oracle.ManagedDataAccess 库),否则无法正确识别 PL/SQL 的关联数组。
具体配置时,需关注以下三个关键点:
OracleParameter.CollectionType 属性必须设置为 OracleCollectionType.PLSQLAssociativeArray,而非 PLSQLNestedTable 等其他选项。OracleParameter.Value 必须是一个 .NET 数组(例如 object[] 或强类型数组),直接传入 List 或 IEnumerable 无效。IN my_table_type 形式,且该 my_table_type 必须是数据库中已定义的 TABLE OF ... 类型。以下是一个配置示例:
var students = new object[] {
new object[] { 1, "Alice", DateTime.Now },
new object[] { 2, "Bob", DateTime.Now }
};
var param = new OracleParameter("p_students", OracleDbType.Object);
param.CollectionType = OracleCollectionType.PLSQLAssociativeArray;
param.UdtTypeName = "STUDENT_TABLE_TYPE"; // 注意:需使用全大写,严格匹配数据库中的定义名
param.Value = students;
若要传递的是自定义对象(如 TestObjType),而不仅是简单字段的组合,则必须让 ODP.NET 知晓如何将 .NET 类与 Oracle 中的对象类型对应起来。仅在 C# 中定义类是不够的,必须进行显式的映射注册。
注册方式主要有两种:
OracleUdtMapping.Add 方法,一次性完成映射。OracleConnection 前手动调用注册方法,此方式易遗漏,不推荐。注册代码通常如下所示(需注意命名空间、属性顺序和大小写的一致性):
[OracleCustomTypeMapping("SCHEMA.TESTOBJTYPE")]
public class TestObjType : IOracleCustomType
{
public decimal ID { get; set; }
public string Name { get; set; }
public DateTime CreateTime { get; set; }
public string Status { get; set; }
public string Description { get; set; }
public void FromCustomObject(OracleConnection con, IntPtr pUdt)
{
// 实现字段写入逻辑
}
public void ToCustomObject(OracleConnection con, IntPtr pUdt)
{
// 实现字段读取逻辑
}
}
// 在应用启动时注册:
OracleUdtMapping.Add(new OracleUdtMapping{
UdtTypeName = "SCHEMA.TESTOBJTYPE",
CSharpTypeName = typeof(TestObjType).AssemblyQualifiedName
});
若遗漏 [OracleCustomTypeMapping] 特性,或注册时类型名大小写不一致(例如数据库中是 TESTOBJTYPE,代码中写为 TestObjType),均可能导致 ORA-03113: end-of-file on communication channel 错误或静默失败。
这是另一个常见问题点:当需要从存储过程中获取 TABLE OF ... 类型的结果集时,若为输出参数预先赋值(如 param.Value = new object[0]),会导致 ODP.NET 尝试向 Oracle 写入该空数组,从而引发类型不匹配错误。
正确的操作步骤如下:
Direction = ParameterDirection.OutputValue 属性(保持为空)param.Value 属性中读取返回的 OracleUdt 或 object[] 数组假设存储过程定义如下:
PROCEDURE get_students(p_out OUT student_table_type)
则 C# 端代码应如下编写:
var outParam = new OracleParameter("p_out", OracleDbType.Object);
outParam.Direction = ParameterDirection.Output;
outParam.UdtTypeName = "STUDENT_TABLE_TYPE";
outParam.CollectionType = OracleCollectionType.PLSQLAssociativeArray;
cmd.Parameters.Add(outParam);
cmd.ExecuteNonQuery();
// 执行完成后,方可读取结果:
var resultArray = (object[])outParam.Value;
还需注意一个细节:UDT 输出结果中的每个元素默认仍是 OracleUdt 实例,不会自动转换为已定义的 .NET 类,除非已完成完整的序列化逻辑注册。若直接进行强制类型转换,会抛出 InvalidCastException 异常。
侠游戏发布此文仅为了传递信息,不代表侠游戏网站认同其观点或证实其描述