Unable to execute oracle function from c# - c#

I'm having problems executing a function in one of my oracle packages from c#. The following is my code for opening the connection and executing the function:
Decimal firstID = Decimal.Parse("2453699");// This values are just for testing
string secondID = "12345";
Decimal sec = Decimal.Parse("1");
string estatus = "TEXT";
OracleConnection con = new OracleConnection();
con.ConnectionString = "User Id=user;Password=the_pass;Data Source=Data_Source";
con.Open();
string sql = "Package.F_FUNCTION_1";
OracleCommand com = new OracleCommand(sql, con);
com.CommandType = System.Data.CommandType.StoredProcedure;
com.Parameters.Add("returnVal", OracleDbType.Varchar2, 32767);
com.Parameters["returnVal"].Direction = System.Data.ParameterDirection.ReturnValue;
com.Parameters.Add("v_firstID",OracleDbType.Decimal,10);
com.Parameters.Add("v_secondID", OracleDbType.Varchar2,200);
com.Parameters.Add("p_sec", OracleDbType.Decimal, 3);
com.Parameters.Add("p_estatus", OracleDbType.Varchar2,50);
com.Parameters["v_firstID"].Value = firstID;
com.Parameters["v_secondID"].Value = secondID;
com.Parameters["p_sec"].Value = sec;
com.Parameters["p_estatus"].Value = estatus;
com.ExecuteNonQuery();
string val = com.Parameters["returnVal"].Value.ToString();
con.Close();
And the following is the function in my package, which needs to call a second function in the same package:
FUNCTION F_FUNCTION_1(v_firstID IN NUMBER,
v_secondID IN VARCHAR2
,p_sec IN NUMBER DEFAULT NULL
,p_estatus IN VARCHAR2 DEFAULT NULL)
RETURN VARCHAR2 AS
v_Return VARCHAR2(200) := '';
secuencia VARCHAR2(1000) := null;
secc VARCHAR2(1000) := NULL;
BEGIN
-- Some validations
v_Return := Package.F_FUNCTION_2(
v_secondID => v_secondID,
P_SEC => p_sec,
P_ESTATUS => p_estatus
);
return v_Return;
END F_FUNCTION_1;
FUNCTION F_FUNCTION_2(v_secondID IN VARCHAR2
,p_sec IN NUMBER DEFAULT NULL
,p_estatus IN VARCHAR2 DEFAULT NULL)
RETURN VARCHAR2 AS
--some variables
v_URL VARCHAR2(500) := NULL;
BEGIN
--A lot of code here that works
RETURN v_URL;
END F_FUNCTION_2;
The problem is: when i run this code, i get the error:
ORA-01460: unimplemented or unreasonable conversion requested
I suppose it is related to the parameters types. I'v tried changing types, using ExecuteScalar instead of ExecuteNonQuery, changing the syntax of the parameters addition to the command... but they didn't work.
I know it's easy but I have a brain lock now.
Hope someone can help.

For this to work you need to add a parameter to your oracle function as an OUT parameter and then extract the parameter as you are doing after using ExecuteNonQuery. I would add the OUT parameter as the first one in your F_FUNCTION_1 function.

Related

.NET/C#: NullReferenceException inside Oracle.ManagedDataAccess.Client.OracleCommand when calling stored procedure with IN OUT parameters

Using the following stored procedure:
create or replace procedure sp_param_test(p_1 number, p_2 in out number) as
begin
p_2 := p_2 * 2;
end;
And the following C# code:
using var connection = new OracleConnection("Data Source=localhost/ORCL;User ID=...");
connection.Open();
using var command = connection.CreateCommand();
command.CommandText = "call sp_param_test(:p_1, :p_2)";
var p_2 = command.CreateParameter();
p_2.ParameterName = "p_2";
p_2.Direction = ParameterDirection.InputOutput;
p_2.Value = 123;
command.Parameters.Add(p_2);
var p_1 = command.CreateParameter();
p_1.ParameterName = "p_1";
p_1.Value = 0;
command.Parameters.Add(p_1);
command.ExecuteNonQuery();
var value = p_2.Value;
connection.Close();
A NullReferenceException is thrown by ExecuteNonQuery. If the parameter is changed to an input-only parameter (remove the "IN OUT" specification) and parameter direction removed from C#, the call works.
The reason the first parameter is unused is I'm trying to call the procedure using named parameter (eg. "p_1 => :p1") but before I can do that I need to resolve the null reference issue.
Anyone seen this behavior before? (using Oracle.ManagedDataAccess.Core version 2.19.100)
BTW. Using CommandType = StoredProcedure works, but the order of parameters needs to be known by the .NET code (the thing I'm trying to avoid). The parameter names are ignored and only the order of calls to Parameters.Add is used to construct the call)

PL/SQL - Oracle: Stored procedure returning dataset from various tables

What I need:
In PLS/SQL on an Oracle DB, create a stored procedure or function with parameters, which given a declared table of , where is a ROW of a table (with all the fields), returns the resultset following the conditions given in the parameters. After, I need to call them from Microsoft Entity Framework with edmx file.
Basically the need is to being able to provide a quick report of the table contents into a pdf, matching some filters, with an oracle db.
The mantainer must be able, provided a script I give, to create and add new reports, so this needs to be dynamic.
Here's what I've got so far:
CREATE OR REPLACE type THETABLEIWANTTYPE as table of THETABLEIWANT%TYPE
create function
SCHEMA.THETABLEIWANT_FUNCTION(PARAM_GR in number default 1)
return THETABLEIWANTTYPE
PIPELINED
as
result_table THETABLEIWANTTYPE
begin
SELECT S.id, S.idg, S.sta, S.tab
Bulk collect into result_table
from SCHEMA.THETABLEIWANT S
WHERE IDGR = PARAM_GR
IF result_table.count > 0 THEN
for i in result_table.FIRST .. result_table.LAST loop
pipe row (result_table(i))
end loop
end if
return
end;
But it's not working. It gives errors.
Running CREATE TYPE I get:
Compilation errors for TYPE SCHEMA.THETABLEIWANT
Error: PLS-00329: schema-level type has illegal reference to
SCHEMA.THETABLEIWANT
The mantainer will launch the script creating a TYPE of the row of the table I need, then the function should return a table with the records.
Then calling it from Entity Framework I should be able to execute it like I'm calling a normal select from my table, IE:
``_dbContext.THETABLEIWANT.Where(x => x.IDGR = Param_gr).ToList();
The problem is that mantainers should be able to generate new kind of reports with any select inside without the need of my intervention on the software code.
Any hint?
It's ok also to bulk all the select result into a temp table but it has to be dynamic as column will be changing
I ended up to write a PLS/SQL procedure that returns a cursor and managing it from C# code with Oracle.ManagedDataAccess Library.
Here's the procedure, for anyone interested:
CREATE OR REPLACE PROCEDURE SCHEMA.PROC_NAME(
PARAM_1 VARCHAR2,
RESULT OUT SYS_REFCURSOR)
IS
BEGIN
OPEN RESULT FOR
SELECT A, V, C AS MY_ALIAS from SCHEMA.TABLE WHERE FIELD = PARAM_1 AND FIELD_2 = 'X';
END;
And here's the C# code for calling and getting the result:
OracleConnection conn = new OracleConnection("CONNECTIONSTRING");
try
{
if (conn.State != ConnectionState.Open)
conn.Open();
List<OracleParameter> parametri = new List<OracleParameter>()
{
new OracleParameter
{
ParameterName = nameof(filter.PARAM_1),
Direction = ParameterDirection.Input,
OracleDbType = OracleDbType.NVarchar2,
Value = filter.PARAM_1
}
};
OracleCommand cmd = conn.CreateCommand();
cmd.Parameters.AddRange(parametri.ToArray());
OracleParameter cursor = cmd.Parameters.Add(
new OracleParameter
{
ParameterName = "RESULT",
Direction = ParameterDirection.Output,
OracleDbType = OracleDbType.RefCursor
}
);
cmd.CommandText = procedureName;
cmd.CommandType = CommandType.StoredProcedure;
cmd.ExecuteNonQuery();
using (OracleDataReader reader = ((OracleRefCursor)cursor.Value).GetDataReader())
{
if (reader.HasRows)
while (reader.Read())
{
//Iterate the result set
}
}
}
catch(Exception ex)
{
//Manage exception
}

C# calling Oracle Function - Error: "Invalid operation on null data"

I'm getting an error "Invalid operation on null data" when my C# code is calling Oracle Function. This happens only if no data is found. If data is found and function returns a value, then everything works ok. I'm a little confused, as - to my understanding at least - function should return 100 if no data found (see function exception).
Oracle Function:
create or replace FUNCTION F_SCO_DPD
(
p_tip IN NUMBER,
p_dav IN VARCHAR2
)
RETURN NUMBER
IS
sco NUMBER;
BEGIN
SELECT max(score) keep(dense_rank first order by vrednost)
INTO sco
FROM sco_sif_score
WHERE sif_kat = 11
AND tip_pod = p_tip
AND vrednost >= (SELECT a.dpd
FROM sco_dpd a
WHERE a.par_davcna = p_dav);
RETURN sco;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
RETURN 100;
END F_SCO_DPD;
C# Code:
using (OracleCommand cmd = new OracleCommand())
{
cmd.Connection = conn;
cmd.CommandText = "F_SCO_DPD";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new OracleParameter("p_tip", Podjetje.TipSub));
cmd.Parameters.Add(new OracleParameter("p_dav", Podjetje.Davcna));
cmd.Parameters.Add(new OracleParameter("sco", OracleDbType.Decimal, ParameterDirection.ReturnValue));
cmd.BindByName = true;
cmd.ExecuteScalar();
Score.ScoDpd = (int)(OracleDecimal)cmd.Parameters["sco"].Value;
}
You are running an aggregation function. max(score) keep (dense_rank first order by vrednost) is as much an aggregation function as max(score).
That means that your query is an aggregation query with no GROUP BY. All such queries return exactly 1 row. If no rows match the WHERE clause, then the value is NULL.
So, the exception is never triggered. Instead, check if the returned value is NULL.
The resulting code is:
create or replace FUNCTION F_SCO_DPD
(
p_tip IN NUMBER,
p_dav IN VARCHAR2
)
RETURN NUMBER
IS
v_sco NUMBER;
BEGIN
SELECT max(score) keep (dense_rank first order by vrednost)
INTO v_sco
FROM sco_sif_score
WHERE sif_kat = 11 AND
tip_pod = p_tip AND
vrednost >= (SELECT a.dpd
FROM sco_dpd a
WHERE a.par_davcna = p_dav
);
RETURN COALESCE(v_sco, 100);
END F_SCO_DPD;

stored procedure not working properly error converting datatype

Trying to get Username by UserId - I have the following stored procedure:
ALTER PROCEDURE [dbo].[GET_UsernameByUserId_SP](
#UserId int,
#ExecutionResult nvarchar(64) OUTPUT
)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
SET #ExecutionResult = (SELECT TOP 1 Username FROM UserProfile WHERE UserId = #Userid);
END
Executed by the following method:
public string CallSpRetStr(String spName, SqlParameter[] sqlParams)
{
string sRet = null;
myCommand.CommandType = CommandType.StoredProcedure;
myCommand.CommandText = spName;
myCommand.Parameters.Clear();
myCommand.Parameters.AddRange(sqlParams);
myCommand.Parameters.AddWithValue("#ExecutionResult", DbType.String);
myCommand.Parameters[myCommand.Parameters.Count - 1].Direction = ParameterDirection.Output;
try
{
if (myConnection.State == ConnectionState.Open)
{
myCommand.ExecuteNonQuery();
}
else
{
OpenConnection();
myCommand.ExecuteNonQuery();
CloseConnection();
}
sRet = myCommand.Parameters["#ExecutionResult"].Value.ToString();
}
catch (Exception ex)
{
CloseConnection();
}
return sRet;
}
Called by the following method:
public string GetUsernameByUserId(int UserId)
{
SqlParameter[] parameters = new SqlParameter[1];
parameters[0] = new SqlParameter("#UserId", UserId);
return dal.CallSpRetStr("GET_UsernameByUserId_SP", parameters);
}
At runtime I get the following error message (caught by the try-catch in CallSpRetStr):
'Error converting datatype nvarchar to int'.
I've been banging my head in the wall for more than hour now, trying crazy things etc.
I have two questions:
1. Does anyone understand what is the problem in all the above?
2. Is anyone aware of a better way to get the username by the userid?
Thanks in advance.
This line is incorrect
myCommand.Parameters.AddWithValue("#ExecutionResult", DbType.String);
should be
myCommand.Parameters.AddWithValue("#ExecutionResult", new string(' ', 64);
The AddWithValue method expects, for its second parameter, the current value for the named parameter but you pass an enum (DbType.String == (int)16). AddWithValue then tries to build a parameter with the datatype corresponding to the value passed and thus creates an integer parameter. Of course this is not acceptable by your stored procedure that expects a nvarchar type
Also I would remove any possible misunderstanding on which parameter is the output one using the return value from the AddWithValue instead of an indexing on the parameter collection
SqlParameter p myCommand.Parameters.AddWithValue("#ExecutionResult", new string(' ', 64);
p.Direction = ParameterDirection.Output;
Notice that we need to create a string of the correct size because AddWithValue doesn't know the expected size of the parameter from the stored procedure and so it creates the parameter with the size equals to the length of the string passed.

Invoke Oracle stored procedure with param input varying array from C#

I have a stored procedure in Oracle which receives an input parameter of type varchar2 varying array. The procedure works and if you invoke it from SQL, what I need is called from C#.
My script is this:
CREATE OR REPLACE PROCEDURE INTEGRATOR.PRC_TEST_PARAM_ARRAY (p_nros_moviles integrator.NROMOVIL_ARRAY) IS
BEGIN
FOR i IN 1..p_nros_moviles.count LOOP
IF p_nros_moviles(i) IS NOT NULL THEN
INSERT INTO INTEGRATOR.TEST_PARAM_ARRAY VALUES (p_nros_moviles(i));
END IF;
END LOOP;
END;
/
My user type:
CREATE OR REPLACE TYPE INTEGRATOR.NROMOVIL_ARRAY AS
VARYING ARRAY(100) OF VARCHAR2(15);
/
My invoke from PLSQL
DECLARE
v_array integrator.NROMOVIL_ARRAY;
BEGIN
v_array := integrator.NROMOVIL_ARRAY('9999999', '66666666');
integrator.prc_test_param_array(v_array);
END;
And I try this way from c#
try
{
using (OracleConnection connection = new OracleConnection())
{
connection.ConnectionString = "Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)" +
"(HOST=10.10.10.10)(PORT=1521)))(CONNECT_DATA=(SERVER=DEDICATED)" +
"(SID=PORTANODE)));User Id=user;Password=*****;";
using (OracleCommand cmd = new OracleCommand("INTEGRATOR.PRC_TEST_PARAM_ARRAY", connection))
{
cmd.CommandType = CommandType.StoredProcedure;
OracleParameter p = new OracleParameter();
p.ParameterName = "P_NROS_MOVILES";
p.OracleDbType = OracleDbType.Array;
p.Direction = ParameterDirection.Input;
p.UdtTypeName = "INTEGRATOR.NROMOVIL_ARRAY";
//p.CollectionType = OracleCollectionType.PLSQLAssociativeArray;
p.Value = new string[] { "XXXX", "YYYY" };
cmd.Parameters.Add(p);
connection.Open();
cmd.ExecuteNonQuery();
MessageBox.Show("Ejecutado");
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
Someone could guide me I need to change to make it work
Be patient, Wait and wait.. it takes hell of a long time .. that is my experience
I'm not sure but I think that System.Data.OracleClient doesn't really support user defined arrays.
I'd try to write a helping stored function, which takes for example a comma separated string (these will be the values of your varray type), and splits it to values using WHILE LOOP and SUBSTR. Then in each iteration it adds the actual VARCHAR2 to a temporary integrator.NROMOVIL_ARRAY type variable using the EXTEND(1) to make place for the new value.
In the end the function returns the temporary integrator.NROMOVIL_ARRAY, and this value could be used in the stored procedure.

Categories