Oracle 9 provides real nice options to keep collection of values.
SYS.ODCINUMBERLIST – VARRAY(32767) of NUMBER
SYS.ODCIVARCHAR2LIST – VARRAY(32767) OF VARCHAR2(4000)
Using these we can pass collection of Numbers / Strings to Stored Procedure.
Now, how could we use this at .Net Layer?
Here are the steps,
Create an Oracle Custom Type for replicate ODCINUMBERLIST / ODCIVARCHAR2LIST.
Map it to appropriate Oracle Types.
Use this Custom Type to pass parameter.
Create Oracle Custom Type.
Make sure a reference “Oracle.DataAccess”.
using System;
using Oracle.DataAccess.Client;
using Oracle.DataAccess.Types;
namespace MyDataAccessUtilities.OracleCustomTypes
{
public class OdciNumberList: IOracleCustomType, INullable
{
[OracleArrayMapping] public Int32[] Array;
public OracleUdtStatus[] StatusArray
{
get;
set;
}
public void FromCustomObject(OracleConnection con, IntPtr pUdt)
{
OracleUdt.SetValue(con, pUdt, 0, Array, StatusArray);
}
public void ToCustomObject(OracleConnection con, IntPtr pUdt)
{
object objectStatusArray;
Array = (Int32[]) OracleUdt.GetValue(con, pUdt, 0, out objectStatusArray);
StatusArray = (OracleUdtStatus[]) objectStatusArray;
}
public bool IsNull
{
get;
private set;
}
public static OdciNumberList Null
{
get
{
var odciNumberList = new OdciNumberList();
odciNumberList.IsNull = true;
return odciNumberList;
}
}
}
[OracleCustomTypeMapping("SYS.ODCINUMBERLIST")]
public class OdciNumberListFactory: IOracleCustomTypeFactory, IOracleArrayTypeFactory
{
public IOracleCustomType CreateObject()
{
return new OdciNumberList();
}
public Array CreateArray(int numElems)
{
return new Int32[numElems];
}
public Array CreateStatusArray(int numElems)
{
return new OracleUdtStatus[numElems];
}
}
} Extension Method to Add this custom type as Parameter value:using System;
using System.Collections.Generic;
using System.Data;
using Oracle.DataAccess.Client;
namespace MyDataAccessUtilities.OracleCustomTypes
{
public static class OracleParameterExtension
{
public static OracleParameter GenerateOdciNumberListParameter(this OracleParameter oracleParameter, string parameterName, List < Int32 > numberList, ParameterDirection parameterDirection = ParameterDirection.Input)
{
oracleParameter.OracleDbType = OracleDbType.Array;
oracleParameter.ParameterName = parameterName;
oracleParameter.UdtTypeName = "SYS.ODCINUMBERLIST";
oracleParameter.Direction = parameterDirection;
if (parameterDirection == ParameterDirection.Input || parameterDirection == ParameterDirection.InputOutput)
{
var numberArray = new OdciNumberList();
if (numberList != null)
{
numberArray.Array = numberList.ToArray();
}
oracleParameter.Value = numberArray;
}
return oracleParameter;
}
public static List < int > GetOdciNumberListParameterValue(this OracleParameter oracleParameter)
{
var result = new List < int > ();
if (oracleParameter.Value is OdciNumberList)
{
var parameter = oracleParameter.Value as OdciNumberList;
if (parameter.Array != null) result.AddRange(parameter.Array);
}
return result;
}
}
}That’s it.Here is sample procedure which takes SYS.ODCINUMBERLIST as input parameter:
CREATE OR REPLACE
PROCEDURE GETEMPLOYEES(PIN_EMPLOYEEIDS SYS.ODCINUMBERLIST, POUT_EMPLOYEES OUT SYS_REFCURSOR)
AS
BEGIN
SELECT EMP.*FROM EMPLOYEE EMP
JOIN TABLE(PIN_EMPLOYEEIDS) IDS
ON EMP.EMP_ID = IDS.COLUMN_VALUE;
ENDHere is the .Net code to call this procedure.var employeeIds = new List < int >
{
100,
110,
115,
116,
117,
118,
119,
120,
124,
125
};
using(var con = new OracleConnection("Connection sting here"))
{
con.Open();
using(var command = new OracleCommand("GetEmployees", con))
{
command.Parameters.Add(new OracleParameter().GenerateOdciNumberListParameter("PIN_EMPLOYEEIDS", employeeIds, ParameterDirection.Input));
command.Parameters.Add(new OracleParameter("POUT_EMPLOYEES", OracleDbType.RefCursor, ParameterDirection.Output));
using(var reader = command.ExecuteReader())
{
// Reader Code here.....
}
}
}