C#DataTable到Oracle存储过程

时间:2015-08-08 07:23:52

标签: c# oracle stored-procedures

我需要做的是将C#DataTable传递给Oracle存储过程。

这就是我所做的:

Oracle方面:

  1. 创建了一个类型:

    create or replace TYPE CUSTOM_TYPE AS OBJECT 
    ( 
        attribute1 VARCHAR(10),
        attribute2 VARCHAR(10)
    );
    
  2. 创建了一个表

    create or replace TYPE CUSTOM_TYPE_ARRAY AS TABLE OF CUSTOM_TYPE;
    
  3. 创建了一个存储过程

    create or replace PROCEDURE SP_TEST
    (
        P_TABLE_IN IN CUSTOM_TYPE_ARRAY,
        P_RESULT_OUT OUT SYS_REFCURSOR 
    ) AS 
    --P_TABLE_IN CUSTOM_TYPE_ARRAY;
    BEGIN
        OPEN P_RESULT_OUT FOR
    
        SELECT attribute1, attribute2
        FROM TABLE(P_TABLE_IN);
    END SP_TEST;
    
  4. C#方:

    void Run()
    {
            OracleConnection oraConn = new OracleConnection();
            oraConn.ConnectionString = ConfigurationManager.ConnectionStrings["NafasV2ConnectionString"].ToString();
            DataSet dataset = new DataSet();
            DataTable Dt = new DataTable();
            OracleDataAdapter da = new OracleDataAdapter();
            OracleCommand cmd = new OracleCommand();
    
            try
            {
                FormTVP(ref Dt);
                PopulateTVP(ref Dt);
                oraConn.Open();
                cmd.Connection = oraConn;
                cmd.CommandType = System.Data.CommandType.StoredProcedure;
                cmd.CommandText = "SP_TEST";
    
                OracleParameter parm1 = new OracleParameter("P_TABLE_IN", OracleDbType.RefCursor,100,"xx");
                parm1.Value = Dt;
                parm1.Direction = ParameterDirection.Input;
                cmd.Parameters.Add(parm1);
    
                OracleParameter parm2 = new OracleParameter("P_RESULT_OUT", OracleDbType.RefCursor);
                parm2.Direction = ParameterDirection.Output;
                cmd.Parameters.Add(parm2);
    
                da.SelectCommand = cmd;
                da.Fill(dataset);
                ASPxLabel1.Text = "OK!!";
            }
            catch (Exception ex)
            {
                ASPxLabel1.Text = "DIE. REASON: " + ex.Message;
            }
            finally
            {
                da.Dispose();
                cmd.Dispose();
                oraConn.Close();
                oraConn.Dispose();
            }
    
        }
    
        void FormTVP(ref DataTable Dt)
        {
            DataColumn attribute1 = Dt.Columns.Add("ATTRIBUTE1", typeof(String));
            DataColumn attribute2 = Dt.Columns.Add("ATTRIBUTE2", typeof(String));
            Dt.AcceptChanges();
        }
    
        void PopulateTVP(ref DataTable Dt)
        {
            DataRow Dr = Dt.NewRow();
            Dr["ATTRIBUTE1"] = "MK1";
            Dr["ATTRIBUTE2"] = "MK2";
            Dt.Rows.Add(Dr);
    
            DataRow Dr1 = Dt.NewRow();
            Dr1["ATTRIBUTE1"] = "HH1";
            Dr1["ATTRIBUTE2"] = "HH2";
            Dt.Rows.Add(Dr1);
    
            Dt.AcceptChanges();
        }
    

    但是我收到了一个错误:

      

    参数绑定参数无效参数名称:P_TABLE_IN

    HELP!

1 个答案:

答案 0 :(得分:3)

无法直接绑定DataTable。您需要为要从.NET访问的任何UDT创建自定义类。在这里,我做了一个简单的例子,说明如何以半通用的方式将DataTable映射到UDT:

void Main()
{
    var dataTable = BuildSourceData();

    using (var connection = new OracleConnection("DATA SOURCE=hq_pdb_tcp;PASSWORD=oracle;USER ID=HUSQVIK"))
    {
        connection.Open();

        using (var command = connection.CreateCommand())
        {
            command.CommandText = "BEGIN HUSQVIK.SP_TEST(:P_TABLE_IN, :P_RESULT_OUT); END;";
            command.BindByName = true;

            var p1 = command.CreateParameter();
            p1.ParameterName = "P_TABLE_IN";
            p1.OracleDbType = OracleDbType.Array;
            p1.UdtTypeName = "HUSQVIK.CUSTOM_TYPE_ARRAY";
            p1.Value = ConvertDataTableToUdt<CustomTypeArray, CustomType>(dataTable);
            command.Parameters.Add(p1);

            var p2 = command.CreateParameter();
            p2.Direction = ParameterDirection.Output;
            p2.ParameterName = "P_RESULT_OUT";
            p2.OracleDbType = OracleDbType.RefCursor;
            command.Parameters.Add(p2);

            command.ExecuteNonQuery();

            using (var reader = ((OracleRefCursor)p2.Value).GetDataReader())
            {
                var row = 1;
                while (reader.Read())
                {
                    Console.WriteLine($"Row {row++}: Attribute1 = {reader[0]}, Attribute1 = {reader[1]}");
                }
            }
        }
    }
}

private DataTable BuildSourceData()
{
    var dataTable = new DataTable("CustomTypeArray");
    dataTable.Columns.Add(new DataColumn("Attribute1", typeof(string)));
    dataTable.Columns.Add(new DataColumn("Attribute2", typeof(string)));

    dataTable.Rows.Add("r1 c1", "r1 c2");
    dataTable.Rows.Add("r2 c1", "r2 c2");

    return dataTable;
}

public static object ConvertDataTableToUdt<TUdtTable, TUdtItem>(DataTable dataTable) where TUdtTable : CustomCollectionTypeBase<TUdtTable, TUdtItem>, new() where TUdtItem : CustomTypeBase<TUdtItem>, new()
{
    var tableUdt = Activator.CreateInstance<TUdtTable>();
    tableUdt.Values = (TUdtItem[])tableUdt.CreateArray(dataTable.Rows.Count);
    var fields = typeof(TUdtItem).GetFields();

    for (var i = 0; i < dataTable.Rows.Count; i++)
    {
        var itemUdt = Activator.CreateInstance<TUdtItem>();
        for (var j = 0; j < fields.Length; j++)
        {
            fields[j].SetValue(itemUdt, dataTable.Rows[i][j]);
        }

        tableUdt.Values[i] = itemUdt;
    }

    return tableUdt;
}

[OracleCustomTypeMapping("HUSQVIK.CUSTOM_TYPE_ARRAY")]
public class CustomTypeArray : CustomCollectionTypeBase<CustomTypeArray, CustomType>
{
}

[OracleCustomTypeMapping("HUSQVIK.CUSTOM_TYPE")]
public class CustomType : CustomTypeBase<CustomType>
{
    [OracleObjectMapping("ATTRIBUTE1")]
    public string Attribute1;
    [OracleObjectMapping("ATTRIBUTE2")]
    public string Attribute2;

    public override void FromCustomObject(OracleConnection connection, IntPtr pointerUdt)
    {
        OracleUdt.SetValue(connection, pointerUdt, "ATTRIBUTE1", Attribute1);
        OracleUdt.SetValue(connection, pointerUdt, "ATTRIBUTE2", Attribute2);
    }

    public override void ToCustomObject(OracleConnection connection, IntPtr pointerUdt)
    {
        Attribute1 = (string)OracleUdt.GetValue(connection, pointerUdt, "ATTRIBUTE1");
        Attribute2 = (string)OracleUdt.GetValue(connection, pointerUdt, "ATTRIBUTE2");
    }
}

public abstract class CustomCollectionTypeBase<TType, TValue> : CustomTypeBase<TType>, IOracleArrayTypeFactory where TType : CustomTypeBase<TType>, new()
{
    [OracleArrayMapping()]
    public TValue[] Values;

    public override void FromCustomObject(OracleConnection connection, IntPtr pointerUdt)
    {
        OracleUdt.SetValue(connection, pointerUdt, 0, Values);
    }

    public override void ToCustomObject(OracleConnection connection, IntPtr pointerUdt)
    {
        Values = (TValue[])OracleUdt.GetValue(connection, pointerUdt, 0);
    }

    public Array CreateArray(int numElems)
    {
        return new TValue[numElems];
    }

    public Array CreateStatusArray(int numElems)
    {
        return null;
    }
}

public abstract class CustomTypeBase<T> : IOracleCustomType, IOracleCustomTypeFactory, INullable where T : CustomTypeBase<T>, new()
{
    private bool _isNull;

    public IOracleCustomType CreateObject()
    {
        return new T();
    }

    public abstract void FromCustomObject(OracleConnection connection, IntPtr pointerUdt);

    public abstract void ToCustomObject(OracleConnection connection, IntPtr pointerUdt);

    public bool IsNull
    {
        get { return this._isNull; }
    }

    public static T Null
    {
        get { return new T { _isNull = true }; }
    }
}

函数ConvertDataTypeToUdt是通用的,如果提供适当的类,它会自动映射数据表。下一步将完全自动化映射,因此目标数据类型将由数据表本身定义。自定义类型属性中的“HUSQVIK”是架构名称,如果您未作为包含自定义类型的架构的所有者进行连接,则它必须与您的数据库对应。