You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

对象类型存储过程参数绑定无效问题求助

解决Oracle UDT类型存储过程参数绑定无效问题

问题情况

用C#调用Oracle存储过程时,直接把DataTable赋值给UDT类型的参数,抛出了以下异常:

OracleInternal.ServiceObjects.OracleParameterImpl.SetUDTDataInBytes(Oracle.ManagedDataAccess.Client.OracleConnection,
OracleInternal.UDT.Types.UDTNamedType, object, out byte[], out object)
OracleInternal.ServiceObjects.OracleParameterImpl.SetUDTDataInBytes(Oracle.ManagedDataAccess.Client.OracleConnection,
OracleInternal.UDT.Types.UDTNamedType, object)
Oracle.ManagedDataAccess.Client.OracleParameter.PreBind_UDT(Oracle.ManagedDataAccess.Client.OracleConnection)
Oracle.ManagedDataAccess.Client.OracleParameter.PreBind(OracleInternal.ServiceObjects.OracleConnectionImpl,
OracleInternal.Common.ColumnDescribeInfo, ref bool, int, out
OracleInternal.Common.ColumnDescribeInfo, out object, bool,
OracleInternal.Common.SqlStatementType)

用到的C#代码如下:

public bool UpdateNumberInBulk_(DataTable dataTable)
{
        try
        {
            using (OracleConnection connection = new OracleConnection(ConnectionString))
            {
                connection.Open();

                using (OracleCommand command = connection.CreateCommand())
                {
                    command.CommandText = "MY_PROCEDURE";
                    command.CommandType = CommandType.StoredProcedure;

                    // 创建表参数对应的OracleParameter
                    OracleParameter parameter = new OracleParameter();
                    parameter.ParameterName = "MY_ARRAY";
                    parameter.OracleDbType = OracleDbType.Object;
                    parameter.Direction = ParameterDirection.Input;
                    parameter.UdtTypeName = "my_type_t";
                    parameter.Value = dataTable;
                    command.Parameters.Add(parameter);

                    // 执行存储过程
                    command.ExecuteNonQuery();
                }
            }

            return true;
        }
        catch (Exception ex)
        {
            throw;
        }
    }

问题根因

Oracle的Managed Data Access无法自动将DataTable映射为自定义UDT类型(my_type_t),直接赋值必然报错。UDT参数必须对应Oracle端定义的对象/集合类型,要么在.NET端实现类型映射,要么改用Oracle支持的参数传递方式。

解决办法

办法1:改用Oracle数组类型传递(适合集合类参数)

如果MY_ARRAY是Oracle的嵌套表/数组类型(非单个对象),可以将参数类型改为OracleDbType.Array,把DataTable转换为Oracle可识别的格式:

  1. 确认Oracle端的my_type_t是集合类型,例如:TYPE my_type_t IS TABLE OF my_object_type
  2. 修改参数绑定代码:
// 将DataTable转换为OracleDataReader
using (OracleDataReader reader = dataTable.CreateDataReader())
{
    parameter.OracleDbType = OracleDbType.Array;
    parameter.UdtTypeName = "my_type_t"; // 建议使用全限定名,如SCHEMA.my_type_t
    parameter.Value = reader;
}

办法2:实现UDT映射类(适合单个对象或自定义集合)

如果my_type_t是自定义对象类型,需要在C#中实现对应的映射类对接Oracle UDT:

  1. 定义与Oracle对象匹配的C#类:
[OracleCustomTypeMapping("my_type_t")] // 建议使用全限定名
public class MyType : IOracleCustomType, INullable
{
    private bool _isNull;

    // 对应Oracle对象的字段,需与Oracle端定义一致
    [OracleObjectMapping("COLUMN1")]
    public int Column1 { get; set; }

    [OracleObjectMapping("COLUMN2")]
    public string Column2 { get; set; }

    public bool IsNull => _isNull;

    public static MyType Null => new MyType { _isNull = true };

    public void FromCustomObject(OracleConnection conn, IntPtr pUdt)
    {
        OracleUdt.SetValue(conn, pUdt, "COLUMN1", Column1);
        OracleUdt.SetValue(conn, pUdt, "COLUMN2", Column2);
    }

    public void ToCustomObject(OracleConnection conn, IntPtr pUdt)
    {
        Column1 = (int)OracleUdt.GetValue(conn, pUdt, "COLUMN1");
        Column2 = (string)OracleUdt.GetValue(conn, pUdt, "COLUMN2");
    }
}

// 配套的工厂类
public class MyTypeFactory : IOracleCustomTypeFactory
{
    public IOracleCustomType CreateObject()
    {
        return new MyType();
    }
}
  1. 将DataTable的行转换为MyType对象数组后赋值给参数:
List<MyType> myTypeList = new List<MyType>();
foreach (DataRow row in dataTable.Rows)
{
    MyType obj = new MyType();
    obj.Column1 = (int)row["Column1"]; // 对应DataTable列名
    obj.Column2 = row["Column2"].ToString();
    myTypeList.Add(obj);
}

parameter.OracleDbType = OracleDbType.Object;
parameter.UdtTypeName = "my_type_t";
parameter.Value = myTypeList.ToArray();

办法3:检查UDT名称和权限

  • 务必确保UdtTypeName使用全限定名称(如用户名.my_type_t),避免因权限或命名空间问题导致类型无法识别。
  • 确认执行代码的数据库用户拥有访问my_type_t类型的权限。

办法4:换用OracleBulkCopy(批量更新场景适用)

如果只是批量更新数据,可直接用OracleBulkCopy将DataTable导入临时表,再调用存储过程处理临时表数据,绕开UDT绑定的复杂问题。


内容的提问来源于stack exchange,提问作者Tanweeruddin Beeharry

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 02:17:55