首页 > 数据库 >.NET如何调用带复杂参数的Oracle存储过程_自定义类型

.NET如何调用带复杂参数的Oracle存储过程_自定义类型

来源:互联网 2026-04-17 20:44:03

Oracle UDT 与 Table Type 的数据库前置创建与验证 在 C# 中使用 ODP.NET 调用涉及 Oracle 自定义类型(UDT)或表类型(Table Type)的存储过程时,必须确保数据库端已预先创建并验证相关类型,同时在代码中正确配置参数类型、值和方向,并显式注册 UDT 映

Oracle UDT 与 Table Type 的数据库前置创建与验证

在 C# 中使用 ODP.NET 调用涉及 Oracle 自定义类型(UDT)或表类型(Table Type)的存储过程时,必须确保数据库端已预先创建并验证相关类型,同时在代码中正确配置参数类型、值和方向,并显式注册 UDT 映射关系。任何环节的缺失都可能导致 ORA-04043、ORA-00902 等错误。

数据库类型必须预先存在并有效

若数据库中的对象类型或表类型未正确创建,无论 C# 端的参数配置如何完善,系统都会抛出如 ORA-04043: object ... does not existORA-00902: invalid datatype 的错误。问题的根源在于缺失必要的前提条件。

长期稳定更新的攒劲资源: >>>点此立即查看<<<

.NET如何调用带复杂参数的Oracle存储过程_自定义类型

因此,在编写代码前,务必在数据库端确认以下两点:

  • 对象类型(OBJECT)已通过 CREATE OR REPLACE TYPE ... AS OBJECT 语句成功创建,且状态为 VALID
  • 表类型(TABLE OF ...)已通过 CREATE OR REPLACE TYPE ... AS TABLE OF ... 语句创建,并且其所依赖的基础对象类型必须已存在。

一个常见的错误场景是:先创建了 student_table_type,却遗漏了其依赖的 student_obj_type。这种情况下,程序可能在连接阶段不报错,但在执行环节会因类型解析失败而提示类型不存在。

ODP.NET 中传递 Table Type 需使用 PLSQLAssociativeArray

需注意,已过时的 System.Data.OracleClient 不应再使用。必须使用 Oracle 官方的 ODP.NET(即 Oracle.ManagedDataAccess 库),否则无法正确识别 PL/SQL 的关联数组。

具体配置时,需关注以下三个关键点:

  • OracleParameter.CollectionType 属性必须设置为 OracleCollectionType.PLSQLAssociativeArray,而非 PLSQLNestedTable 等其他选项。
  • OracleParameter.Value 必须是一个 .NET 数组(例如 object[] 或强类型数组),直接传入 ListIEnumerable 无效。
  • 存储过程中对应的参数必须声明为 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;

UDT 对象数组需映射至 .NET 类并注册 OracleUdtMapping

若要传递的是自定义对象(如 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 错误或静默失败。

输出参数为 UDT 时,Direction 需设为 Output 且不预设 Value

这是另一个常见问题点:当需要从存储过程中获取 TABLE OF ... 类型的结果集时,若为输出参数预先赋值(如 param.Value = new object[0]),会导致 ODP.NET 尝试向 Oracle 写入该空数组,从而引发类型不匹配错误。

正确的操作步骤如下:

  • 设置 Direction = ParameterDirection.Output
  • 不设置 Value 属性(保持为空)
  • 命令执行完毕后,再从 param.Value 属性中读取返回的 OracleUdtobject[] 数组

假设存储过程定义如下:

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 异常。

侠游戏发布此文仅为了传递信息,不代表侠游戏网站认同其观点或证实其描述

热游推荐

更多
湘ICP备14008430号-1 湘公网安备 43070302000280号
All Rights Reserved
本站为非盈利网站,不接受任何广告。本站所有软件,都由网友
上传,如有侵犯你的版权,请发邮件给xiayx666@163.com
抵制不良色情、反动、暴力游戏。注意自我保护,谨防受骗上当。
适度游戏益脑,沉迷游戏伤身。合理安排时间,享受健康生活。