Call an Oracle Function from C# with Nulls - c#

I'm trying to call an Oracle Function from our C# application, but I either get the following errors. I think I have two problems:
I want to call this function, but some of the parameters can be null on the C# side so I don't know how to handle them.
I don't know if I need to add the return value to the parameters with ParameterDirection.ReturnValue on the OracleParameter object.
This is what I'm trying:
public int GetActivityRowCount(DateTime fromDate, DateTime thruDate, string grpCds, string catCds, string typCds, long? memNbr, long? subNbr, string searchBy, string dispActivity, string statCds, bool showUncategorized, string debugYN)
{
OracleCommand cmd = null;
try
{
StringBuilder sql = new StringBuilder();
sql.Append(" pack_SomePack.func_SearchRowCount");
cmd = new OracleCommand(sql.ToString(), this.Connection);
cmd.CommandType = CommandType.StoredProcedure;
// Don't know if I should add this guy
// cmd.Parameters.Add(new OracleParameter("RowCount", OracleDbType.Int16, ParameterDirection.ReturnValue));
cmd.Parameters.Add(new OracleParameter("FromDate", OracleDbType.Date, fromDate, ParameterDirection.Input));
cmd.Parameters.Add(new OracleParameter("ThruDate", OracleDbType.Date, thruDate, ParameterDirection.Input));
cmd.Parameters.Add(new OracleParameter("grpCds", OracleDbType.Varchar2, grpCds, ParameterDirection.Input));
cmd.Parameters.Add(new OracleParameter("catCds", OracleDbType.Varchar2, catCds, ParameterDirection.Input));
cmd.Parameters.Add(new OracleParameter("typCds", OracleDbType.Varchar2, typCds, ParameterDirection.Input));
cmd.Parameters.Add(new OracleParameter("memNbr", OracleDbType.Long, memNbr, ParameterDirection.Input));
cmd.Parameters.Add(new OracleParameter("SubNbr", OracleDbType.Long, SubNbr, ParameterDirection.Input));
cmd.Parameters.Add(new OracleParameter("searchBy", OracleDbType.Varchar2, searchBy, ParameterDirection.Input));
cmd.Parameters.Add(new OracleParameter("dispActivity", OracleDbType.Varchar2, dispActivity, ParameterDirection.Input));
cmd.Parameters.Add(new OracleParameter("statCds", OracleDbType.Varchar2, statCds, ParameterDirection.Input));
cmd.Parameters.Add(new OracleParameter("showUncategorized", OracleDbType.Char, showUncategorized? "Y" : "N", ParameterDirection.Input));
cmd.Parameters.Add(new OracleParameter("debugYN", OracleDbType.Varchar2, debugYN, ParameterDirection.Input));
cmd.BindByName = true;
int activityRowCount = Convert.ToInt16(cmd.ExecuteScalar()); // Error here
return activityRowCount;
}
My function does the following:
FUNCTION func_SearchRowCount
(
in_FromDate IN DATE,
in_ThruDate IN DATE,
in_GrpCds IN VARCHAR2,
in_CatCds IN VARCHAR2,
in_TypCds IN VARCHAR2,
in_MemNbr IN Actv.PersNbr%TYPE,
in_SubNbr IN Actv.SubNbr%TYPE,
in_SearchBy IN VARCHAR2,
in_dispActivity IN VARCHAR2,
in_StatCds IN Ams.StatCd%TYPE,
in_UncategorizedYN IN CHAR,
in_DebugYN IN CHAR
) RETURN NUMBER AS
lvnCount NUMBER;
lvsSqlStr VARCHAR2(2000);
BEGIN
lvsSqlStr := 'SELECT COUNT(*) FROM SomeTable WHERE (Include a bunch of clauses..)';
BEGIN
EXECUTE IMMEDIATE lvsSqlStr
INTO lvnCount
USING (All the parameters);
END;
RETURN lvnCount;
END func_SearchRowCount;
I get the following error when running what's above.
PLS-00306: wrong number or types of arguments in call to 'FUNC_SEARCHROWCOUNT'
All the variables are bound with the correct amount, although I read somewhere that ODP.NET will remove the parameters with null as there .Value. Is this true? What should I pass in to indicate that there is no value for that parameter then?

You need 4 things at a minimum:
The first two: call ExecuteNonQuery and not ExecuteScalar as discussed in this MSDN thread and create the return value parameter. About a third of the way down on Accessing Oracle 9i Stored Procedures it shows this code and says
You execute the function in the same way as a stored procedure. Use a ParameterDirection.ReturnValue parameter to get the result returned by the function.
On the third, do use DbNull.Value because it is specifically designed as a placeholder to represent null values in a database, whereas null has meaning only for .NET. (Well, null is probably ok because the Oracle driver is probably smart enough to handle it; DbNull.Value is a good habit though because you're being explicit). You can do something like
new OracleParameter("typCds", OracleDbType.Varchar2, typCds ?? (object)DbNull.Value, ParameterDirection.Input));
And finally, you have bind by name on your parameters, but the names don't match the names of your parameters. Match the names exactly or bind by position.
As to the specific error, the return value is "an argument" and didn't bind he parameters correctly. Oracle wanted 13 parameters and you effectively gave it none.

there seems to be several problems with your code.
Oracle Type LONG is not same like in C#, LONG in Oracle DB allows you to store character data up to 2GB size. In C# it's an numeric type using 64 Bit. Since your submitted code does not explain what type of data your parameters in_MemNbr, in_SubNbr and in_StatCds in the package function are, i can only guess, what it is depending on your definitions of the parameter list in your c# method.
Your parameter names in C# in the "new OracleParameter("")" statements does not match the function parameters exactly. In Pl/Sql you added an "in_" prefix, but removed it in the c# code. With "cmd.BindByName = true;" you say to ODP.Net "Hey, bind the parameters in the collection by name rather than using position". In this case they must match exactly.
Your C# return value of the method is int (System.Int32), the return value of the PlSql package function is NUMBER. ODP.Net seems to return decimal in C# in case of numbers without specified scale. Maybe you run into a conversion/invalidcast exception when ODP.Net tries to convert the oracle number type into short (Int16) internally. Maybe you get an out of range exception when the returned count is greater than short.MaxValue. Try to specify Int32 as return value in your return value parameter creation.
OracleCommand implements IDisposable interface. Please ensure that your command is disposed, when not needed anymore, since the implementation of IDisposable interfaces in objects shows you that the object creates/uses some resources (managed or unmanaged) and must release them, when operations finished. Shortest way is to use the
"using" keyword of C#, which ensures a call of cmd.Dispose() when code execution leaves the block regardless if exception occured or block ends with success.
public int GetActivityRowCount(DateTime fromDate, DateTime thruDate, string grpCds, string catCds, string typCds, long? memNbr, long? subNbr, string searchBy, string dispActivity, string statCds, bool showUncategorized, string debugYN)
{
using (var cmd = new OracleCommand("pack_SomePack.func_SearchRowCount", this.Connection))
using (var result = new OracleParameter("result", OracleDbType.Int32, ParameterDirection.ReturnValue))
using (var fromDateParam = new OracleParameter("in_FromDate", OracleDbType.Date, fromDate, ParameterDirection.Input))
using (var thruDateParam = new OracleParameter("in_ThruDate", OracleDbType.Date, thruDate, ParameterDirection.Input))
using (var grpCdsParam = new OracleParameter("in_GrpCds", OracleDbType.Varchar2, grpCds, ParameterDirection.Input))
using (var catCdsParam = new OracleParameter("in_CatCds", OracleDbType.Varchar2, catCds, ParameterDirection.Input))
using (var typCdsParam = new OracleParameter("in_TypCds", OracleDbType.Varchar2, typCds, ParameterDirection.Input))
using (var memNbrParam = new OracleParameter("in_MemNbr", OracleDbType.Int64, memNbr, ParameterDirection.Input))
using (var subNbrParam = new OracleParameter("in_SubNbr", OracleDbType.Int64, SubNbr, ParameterDirection.Input))
using (var searchByParam = new OracleParameter("in_SearchBy", OracleDbType.Varchar2, searchBy, ParameterDirection.Input))
using (var dispActivityParam = new OracleParameter("in_dispActivity", OracleDbType.Varchar2, dispActivity, ParameterDirection.Input))
using (var statCdsParam = new OracleParameter("in_StatCds", OracleDbType.Varchar2, statCds, ParameterDirection.Input))
using (var uncategorizedYnParam = new OracleParameter("in_UncategorizedYN", OracleDbType.Char, showUncategorized ? "Y" : "N", ParameterDirection.Input))
using (var debugYnParam = new OracleParameter("in_DebugYN", OracleDbType.Char, debugYN, ParameterDirection.Input))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(result);
cmd.Parameters.Add(fromDateParam);
cmd.Parameters.Add(thruDateParam);
cmd.Parameters.Add(grpCdsParam);
cmd.Parameters.Add(catCdsParam);
cmd.Parameters.Add(typCdsParam);
cmd.Parameters.Add(memNbrParam);
cmd.Parameters.Add(subNbrParam);
cmd.Parameters.Add(searchByParam);
cmd.Parameters.Add(dispActivityParam);
cmd.Parameters.Add(statCdsParam);
cmd.Parameters.Add(uncategorizedYnParam);
cmd.Parameters.Add(debugYnParam);
cmd.BindByName = true;
cmd.ExecuteNonQuery();
return Convert.ToInt32(result.Value);
}
}

Related

Input string was not in a correct format while calling oracle function from c#

I'm getting this error while calling Oracle function
Input string was not in a correct format.
This is function
create or replace function abs.test_func(test_in in integer)
return integer
is
test_out integer ;
BEGIN
test_out:=test_in;
RETURN test_out;
END;
this is c# code
using (var cmd = new OracleCommand("abs.test_func", conn1))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new OracleParameter("test_in", OracleDbType.Int64, test_in, ParameterDirection.Input));
OracleParameter test_out = new OracleParameter("test_out", OracleDbType.Int64);
test_out.Direction = ParameterDirection.ReturnValue;
cmd.Parameters.Add(test_out);
cmd.ExecuteNonQuery();
JsonResponse.Result = Int32.Parse(test_out.Value.ToString());
I don't know wha I'm doing wrong, i wrote this function specially for testing purposes. It's very simple: 1 variable inputs 1 variable outputs as a function return. that's all. What else can be done here to get it working?
This Helped
cmd.Parameters.Add(new OracleParameter("test_out", OracleDbType.Int64, ParameterDirection.ReturnValue));
cmd.ExecuteNonQuery();
JsonResponse.Result = Int32.Parse(cmd.Parameters[0].Value.ToString());

How to check an output DbParameter for null value?

I'm using System.Data.Common.DbCommand.ExecuteNonQueryAsync to call an Oracle stored procedure that has one output parameter o_err_msg which (you guessed it) is a VARCHAR2 error message when an exception occurs or null when the procedure succeeds. So, after calling `ExecuteNonQueryAsync, I'm trying to check if that parameter is null, to see if there was an error or not. But can't find a way to actually do it.
I've tried comparing (through both the == operator and the Equals method) the Value property of the parameter to null, to DBNull.Value and calling System.Convert.IsDBNull on it. All of these return null. I've even tried converting it to string (either through the ToString method or System.Convert.ToString) and then calling string.IsNullOrEmpty, but the resulting string is "null".
The code I'm running is somewhat similar to this:
DbCommand cmd = dbConnection.CreateCommand();
cmd.CommandText = "PCKG_FOO.PROC_BAR";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new OracleParameter("o_err_msg", OracleDbType.Varchar2, 4000, "", ParameterDirection.Output));
await cmd.ExecuteNonQueryAsync();
if (cmd.Parameters["o_err_msg"].Value != null)
throw new InvalidOperationException(cmd.Parameters["o_err_msg"].Value.ToString());
Even though, in this example, I'm initializing the parameters as OracleParameter, in my code I only have DbParameter accessible and I'd prefer if I didn't have to cast it to OracleParameter or anything Oracle-specific.
Is there any way I can do this?
Thanks in advance.
I encountered the same problem today, the fowling code should be a workarround.
OracleParameter out1 = new OracleParameter("out1", OracleDbType.Varchar2, ParameterDirection.Output);
out1.Size = 1000;
Oracle.ManagedDataAccess.Types.OracleString out1Value = (Oracle.ManagedDataAccess.Types.OracleString)out1.Value;
if (!out1Value.IsNull)
{
// do something
}

Can't insert 0 values using a Parameterized Query

I have an inventory system and this code is for when a user creates a new item. It's supposed to insert a 0 value in the inventory table since it's a new item. My code is:
string queryAdd4 = "INSERT INTO [inventory]([item_id],[item_qty],[item_date],[item_type]) VALUES(#myID,#myQty,#myDate,#myType)";
using (SqlCommand cmd = new SqlCommand(queryAdd4, Con))
{
cmd.Parameters.Add(new SqlParameter("#myID", item_id));
cmd.Parameters.Add(new SqlParameter("#myQty", 0));
cmd.Parameters.Add(new SqlParameter("#myDate", dateNow));
cmd.Parameters.Add(new SqlParameter("#myType", 1));
Con.Open();
cmd.ExecuteNonQuery();
Con.Close();
}
With that code, i'm getting an error saying:
The parameterized query '(#myID int,#myQty bigint,#myDate datetime,#myType int)
INSERT INT' expects the parameter '#myQty', which was not supplied
Out of curiosity, I tried replacing the 0 beside the #myQty with 1 and the query worked without problems. I also tried manually running the query through the Server Explorer and that worked as well. So I'm guessing 0 is not a valid number to insert when using parameterized queries? If so, how would I go about doing it?
When using two parameters with SqlParameter Constructor, there are two choices:
SqlParameter(string parameterName, SqlDbType dbType)
SqlParameter(string parameterName, object value)
When using an integer, the first choice is used. If you want to use the two parameter constructor, you have to cast 0 to an object:
cmd.Parameters.Add(new SqlParameter("#myQty", (object)0));
Also regard the oneliner from Sinatr in the comments:
cmd.Parameters.Add(new SqlParameter("#myQty", 0) { SqlDbType = SqlDbType.Int });
try to set the specific type to your parameter like here;
Take a look at a database and set it according to the type which is set to the column.
string queryAdd4 = "INSERT INTO [inventory]([item_id],[item_qty],[item_date],[item_type]) VALUES(#myID,#myQty,#myDate,#myType)";
using (SqlCommand cmd = new SqlCommand(queryAdd4, Con))
{
cmd.Parameters.Add(new SqlParameter("#myID", item_id));
var parameter = new SqlParameter()
parameter.ParameterName = "#myQty";
parameter.SqlDbType = SqlDbType.Int;
parameter.Direction = ParameterDirection.Input;
parameter.Value = 0;
cmd.Parameters.Add(parameter);
cmd.Parameters.Add(new SqlParameter("#myDate", dateNow));
cmd.Parameters.Add(new SqlParameter("#myType", 1));
Con.Open();
cmd.ExecuteNonQuery();
Con.Close();
Sources:
List of types:
https://msdn.microsoft.com/en-us/library/system.data.sqldbtype(v=vs.110).aspx
Configuring query parameters:
https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/configuring-parameters-and-parameter-data-types
Hope it helps.

Stored procedure expects a parameter I am already passing in

I am trying to execute a stored procedure with this declaration:
ALTER PROCEDURE [dbo].[getByName]
#firstName varchar,
#lastName varchar
AS
...
And I am calling in C# as follows:
public List<Person> GetPersonByName(string first, string last)
{
var people = new List<Person>();
var connString = ConfigurationManager.ConnectionStrings["MyDbConnString"].ConnectionString;
using (var conn = new SqlConnection(connString))
{
using (var cmd = new SqlCommand("dbo.getByName",conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("#firstName", SqlDbType.VarChar, 50)).Value = first;
cmd.Parameters.Add(new SqlParameter("#lastName", SqlDbType.VarChar, 50)).Value = last;
conn.Open();
using (var reader = cmd.ExecuteReader())
{
people = ReadPeopleData(reader);
}
conn.Close();
}
}
return people;
}
But I just get back this error:
Procedure or function 'getByName' expects parameter '#firstName' which was not supplied.
Update:
ALTER PROCEDURE [dbo].[getEmployeesByName]
#firstName varchar(50),
#lastName varchar(50)
AS
...
and stating:
cmd.Parameters.Add(new SqlParameter("#firstName", SqlDbType.VarChar, 50)).Value
for both parameters, yet it continues to throw the exception.
I have seen this issue occur many, many times in two common scenarios:
The value being assigned to the parameter is null (or Nothing in VB.Net). This is the .Net null, not the DB null (DBNull.Value) and this happens most commonly with strings.
The parameter being created is associated with the wrong command object. This commonly occurs when you have multiple command objects in the same class with similar names.
Please double-check the code to ensure that the string variable is not set to null and that the parameter is being added to the correct command.
Update
Based on the full updated code that was posted, the likely issue is 1 above.
To circumvent this problem in your code, add the following at the top of the method:
if (first == null) {
first = "";
}
if (last == null) {
last = "";
}
Try this it will work:
SqlParameter[] sqlParams = new SqlParameter[] {
new SqlParameter("#UserName",(object)userName ?? DBNull.Value),
new SqlParameter("#Password",(object)password ?? DBNull.Value)
};
If parameter is NULL than replace it with DBNull Type using ?? Operator
Please add CommandaType to SQLCommand.
e.g: scmd.CommandType = CommandType.StoredProcedure;

expects parameter '#ID', which was not supplied?

I am sending ID as outparameter but its giving error
System.Data.SqlClient.SqlException: Procedure or function
'usp_ClientHistoryItem' expects parameter '#ID', which was not
supplied.
Code
using (SqlCommand cmd = new SqlCommand("dbo.usp_ClientHistoryItem", conn))
{
SqlParameter parameterID = new SqlParameter("#ID", oReservation.Id);
parameterID.Direction = ParameterDirection.Output;
cmd.Parameters.Add(parameterID);
cmd.Parameters.Add(new SqlParameter("#PhoneNo", oReservation.ClientPhone));
cmd.Parameters.Add(new SqlParameter("#UserId", oReservation.UserID));
cmd.Parameters.Add(new SqlParameter("#Description", oReservation.Description));
cmd.Parameters.Add(new SqlParameter("#TestId", oReservation.TestId));
cmd.Parameters.Add(new SqlParameter("#StartDate", oReservation.StartDate));
cmd.ExecuteNonQuery();
returnValue = Convert.ToInt32(cmd.Parameters["#ID"].Value);
return returnValue;
}
You seem to be calling a stored procedure - yet you've never defined your SqlCommand to be a stored procedure:
using (SqlCommand cmd = new SqlCommand("dbo.usp_ClientHistoryItem", conn))
{
cmd.CommandType = CommandType.StoredProcedure; // add this line to tell ADO.NET it's a stored procedure!!
If you forget that line, then ADO.NET will try to interpret your stuff as an ad-hoc SQL statement....
this one solve my problem
may be it may helpful
cmd.CommandType = CommandType.StoredProcedure;
Your ID parameter in the stored procedure must be set as OUTPUT parameter. You are just setting it in code not in stored procedure.
Hy guys.
You have to set the property CommandType for the Command to StoredProcedure if that's the case. Otherwise it woun't detect the parameters.
One other reason this error is thrown is when the variable names don't match in your stored procedure and code because the code fails to find the parameter to which the value must be passed. Make sure they match:
Stored procedure:
create procedure getEmployee
#ID
as
Begin
select *
from emp
where id = #ID
End
Code:
SqlParameter p = new SqlParameter("#ID", id);
cmd.Parameter.Add(p);
The parameter #ID must match in both code and stored procedure
If you use dapper, you can use this construction
int id = 1;
var parameters = new DynamicParameters();
parameters.Add("#id", id, DbType.Int32, ParameterDirection.Input);
string sqlQuery = "[dbo].[SomeStoredProcedure]";
using (IDbConnection db = new SqlConnection(ConnectionString))
{
var result = await db.QueryAsync<SpResult>(sqlQuery, parameters, commandType: CommandType.StoredProcedure);
}

Categories

Resources