Can't pass an array to oracle procedure - c#

I've created a procedure for test this problem and it works right in oracle developer. There is a typed named "dizi" (array and varchar2). And procedure has input parameter. I'm trying to pass an array to this to this procedure as a parameter in c#. I've searched a lot but i couldn't solve the problem. The error is: "Not all veriables bound"
public void InsertQuestion(List<string> area_list)
{
quest_areas = area_list.ToArray();
command = new OracleCommand();
command.Connection = connect;
connect.Open();
var arry = command.Parameters.Add("area_array",OracleDbType.Varchar2);
arry.Direction = ParameterDirection.Input;
arry.Size = quest_areas.Length;
arry.CollectionType = OracleCollectionType.PLSQLAssociativeArray;
arry.Value = quest_areas;
command.BindByName = true;
command.CommandText ="TESTPROCEDURE(:area_array)";
command.CommandType = CommandType.StoredProcedure;
command.ExecuteNonQuery();
connect.Close();
}
Here is my procedure (it is just for test but i'll use something like that)
CREATE OR REPLACE PROCEDURE TESTPROCEDURE (t_in IN dizi)
IS
BEGIN
FOR i IN 1..t_in.count LOOP
dbms_output.put_line(t_in(i));
END LOOP;
END;

I've got code that successfully passes array down to oracle sprocs. Takes a slightly different approach to yours. Not entirely sure how much is relevant, but in case it helps my code:
uses the correct name parameter name (t_in in your case)
doesn't bother setting the size of the parameter
creates an object array that is the correct length and copies the contents across into it (ie from quest_areas in your case)
then sets this object array as the Value for the command parameter
doesn't use bind variables when calling the proc, rather just uses the proc name by itself as the CommandText.
That said, I suspect your problem might be around your use of a bind variable when calling the procedure. What happens if you just set 'TESTPROCEDURE' as the CommandText?
Or go the other way and put change it into a proper anonymous PLSQL block 'begin TESTPROCEDURE(:area_array); end;' and change the CommandType to CommandType.Text (as just suggested by Wernfried while I was typing...)
Update
public void InsertQuestion(List<string> area_list)
{
var input_array = area_list.Select(s => (object)s).ToArray();
command = new OracleCommand();
command.Connection = connect;
connect.Open();
var arry = command.Parameters.Add("area_array",OracleDbType.Varchar2);
arry.Direction = ParameterDirection.Input;
arry.CollectionType = OracleCollectionType.PLSQLAssociativeArray;
arry.Value = input_array;
command.CommandText ="TESTPROCEDURE";
command.CommandType = CommandType.StoredProcedure;
command.ExecuteNonQuery();
connect.Close();
}

Related

Call DB2 stored procedure from C#

After reading an interesting article online : Calling DB2 stored procedures from .NET applications
I'd like to share an issue recently encountered with a derived code :
DateTime transa_date = DateTime.ParseExact(trandate, "yyyy-MM-dd",
CultureInfo.InvariantCulture);
DB2Connection conn = new DB2Connection(MyDb2ConnectionString);
conn.Open();
try
{
// MyDb2Connection.Open();
// conn.Open();
// assume a DB2Connection conn
DB2Transaction trans = conn.BeginTransaction();
cmd = conn.CreateCommand();
procName = "MYTBLSCHEMA.TEST";
procCall = "CALL MYTBLSCHEMA.TEST(#NAME, #ADDRESS_LINE, #REGNUM, #TRANSA)";
cmd.Transaction = trans;
cmd.CommandType = System.Data.CommandType.Text;
cmd.CommandText = procCall;
// Register input-output and output parameters for the DB2Command
cmd.Parameters.Add( new DB2Parameter("#NAME", name)); #of string type
cmd.Parameters.Add( new DB2Parameter("#ADDRESS_LINE", adr)); #of string type
cmd.Parameters.Add( new DB2Parameter("#REGNUM", reg)); #of string type
cmd.Parameters.Add( new DB2Parameter("#TRANSA", transa_date)); #of date type (in DB2 table)
// Call the stored procedure
Console.WriteLine(" Call stored procedure named " + procName);
cmd.ExecuteNonQuery();
}
The above code neither generates an exception at cmd.ExecuteNonQuery() nor inserts the (expected) row into the table.
Hence, a Hope to understand through this post the rationale underlying such phenomenon.
Thanks.
N.B: Executing (manually)
CALL MYTBLSCHEMA.TEST('test', 'test_address_', 'test_num', 2021-01-01)
from the IDE does work (e.g. insert the row into the table).
DB2 version: 11.5.6.0.00000.008
I'd either remove this line:
DB2Transaction trans = conn.BeginTransaction();
Or I'd add this line at the end of the try:
trans.Commit();
As to which you'd choose; as it's a single stored procedure, unless there's some internal overriding concern within the sproc that makes sense to have a transaction to be started outside it cover it, I'd remove it. If you have, or plan to have multiple operations that must either all-succeed or all-fail, then I'd keep it/commit it..

WCF - Foreach loop to insert records into a database

I'm working on WCF project. I am trying to insert multiple records into my SQL Server database from an array.
when calling the service, I get an exception :"Procedure or function has too many arguments specified", while my arguments in my function are in confirmity with those declared in my stored procedure :
Here is my function in WCF :
public static string SetGaranties( List<int> CODE_GARANTIES, string NUMERO_POLICE, string CODE_BRANCHE, int CODE_SOUS_BRANCHE)
{
string MSG_ACQUITEMENT = string.Empty;
DbCommand com = GenericData.CreateCommand(GenericData.carte_CarteVie_dbProviderName, GenericData.Carte_CarteVie_dbConnectionString);
com.CommandText = "SetGaranties";
com.Parameters.Clear();
foreach (int CODE_GARANTIE in CODE_GARANTIES)
{
com.Connection.Open();
SqlParameter NUMERO_POLICE_Param = new SqlParameter("#NUMERO_POLICE", NUMERO_POLICE);
com.Parameters.Add(NUMERO_POLICE_Param);
SqlParameter CODE_BRANCHE_Param = new SqlParameter("#CODE_BRANCHE", CODE_BRANCHE);
com.Parameters.Add(CODE_BRANCHE_Param);
SqlParameter CODE_SOUS_BRANCHE_Param = new SqlParameter("#CODE_SOUS_BRANCHE", CODE_SOUS_BRANCHE);
com.Parameters.Add(CODE_SOUS_BRANCHE_Param);
SqlParameter CODE_POSTALE_Param = new SqlParameter("#CODE_GARANTIE", CODE_GARANTIE);
com.Parameters.Add(CODE_POSTALE_Param);
DbDataReader reader = com.ExecuteReader();
com.Connection.Close();
}
and here is my Stored procedure :
ALTER PROCEDURE [dbo].[SetGaranties]
#NUMERO_POLICE varchar(12),
#CODE_BRANCHE varchar(1),
#CODE_SOUS_BRANCHE int,
#CODE_GARANTIE int
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO dbo.MVT_GARANTIES VALUES(
#NUMERO_POLICE,
#CODE_BRANCHE,
#CODE_SOUS_BRANCHE,
#CODE_GARANTIE
);
END
Does anybody know how to fix this?
Build the parameters outside the loop just once, set the invariant values outside the loop and inside the loop just set only the one value that changes at each loop
public static string SetGaranties( List<int> CODE_GARANTIES, string NUMERO_POLICE, string CODE_BRANCHE, int CODE_SOUS_BRANCHE)
{
string MSG_ACQUITEMENT = string.Empty;
DbCommand com = GenericData.CreateCommand(GenericData.carte_CarteVie_dbProviderName, GenericData.Carte_CarteVie_dbConnectionString);
com.CommandText = "SetGaranties";
com.CommandType = CommandType.StoredProcedure;
// These parameter's values don't change, set it once
com.Parameters.Add("#NUMERO_POLICE", SqlDbType.VarChar).Value = NUMERO_POLICE;
com.Parameters.Add("#CODE_BRANCHE",SqlDbType.VarChar).Value = CODE_BRANCHE;
com.Parameters.Add("#CODE_SOUS_BRANCHE", SqlDbType.Int).Value = CODE_SOUS_BRANCHE;
// This parameter's value changes inside the loop
com.Parameters.Add("#CODE_GARANTIE",SqlDbType.Int);
com.Connection.Open();
foreach (int CODE_GARANTIE in CODE_GARANTIES)
{
com.Parameters["#CODE_GARANTIE"].Value = CODE_GARANTIE;
com.ExecuteNonQuery();
}
com.Connection.Close();
}
Other things to say:
You are using a global connection object, this usually is a very bad
idea. ADO.NET implements connection pooling and this means that you
should create your connection when you need it and destroy it
afterwards.
ExecuteNonQuery should be used when you INSERT/UPDATE/DELETE records.
No need to build an SqlDataReader when you don't have anything to
read back.
A Stored Procedure is executed only if you set the CommandType to
StoredProcedure otherwise you get a syntax error because the
CommandText is not a valid Sql Statement
This:
com.Parameters.Clear();
Should be inside your loop. With the current code the first iteration should have the correct number of parameters. But subsequent iterations will have too many because the the param list isn't being cleared.

C# Stored procedure or function expects parameter which is not supplied

I am fairly new to C# and I'm trying to set up call to a stored procedure in my database which takes one parameter.
I get the error "Procedure or function 'SP_getName' expects parameter '#username', which was not supplied. "
My Stored procedure works ok when I supply it with the parameter and I run it via SQL management studio.
GO
DECLARE #return_value int
EXEC #return_value = [dbo].[SP_getName]
#username = 'bob101'
SELECT 'Return Value' = #return_value
GO
However when I try and call it the error is with how I'm passing the parameter in, but I can't spot what the issue is.
//create a sql command object to hold the results of the query
SqlCommand cmd = new SqlCommand();
//and a reader to process the results
SqlDataReader reader;
//Instantiate return string
string returnValue = null;
//execute the stored procedure to return the results
cmd.CommandText = "SP_getName";
//set up the parameters for the stored procedure
cmd.Parameters.Add("#username", SqlDbType.NVarChar).Value = "bob101";
cmd.CommandType = CommandType.Text;
cmd.Connection = this.Connection;
// then call the reader to process the results
reader = cmd.ExecuteReader();
Any help in spotting my error would be greatly appreciated!
I've also tried looking at these two posts, but I haven't had any luck:
Stored procedure or function expects parameter which is not supplied
Procedure or function expects parameter, which was not supplied
Thanks!
You have stated:
cmd.CommandType = CommandType.Text;
Therefore you are simply executing:
SP_getName
Which works because it is the first statement in the batch, so you can call the procedure without EXECUTE, but you aren't actually including the parameter. Change it to
cmd.CommandType = CommandType.StoredProcedure;
Or you can change your CommandText to:
EXECUTE SP_getName #username;
As a side note you should Avoid using the prefix 'sp_' for your stored procedures
And a further side note would be to use using with IDisposable objects to ensure they are disposed of correctly:
using (var connection = new SqlConnection("ConnectionString"))
using (var cmd = new new SqlCommand("SP_getName", connection))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#username", SqlDbType.NVarChar).Value = "bob101";
connection.Open();
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
// Do something
}
}
}
I had this problem, but it wasn't about parameter name of Command Type.
My problem was that when C# calls SP, for each parameter that has no value passes 'default' keyword (i found it in SQL Profiler):
... #IsStop=0,#StopEndDate=default,#Satellite=0, ...
in my case my parameter Type was DateTime :
#StopEndDate datetime
. I Solved my problem by seting default value to this parameter in Stored Procedure :
#StopEndDate datetime=null
Try remove #:
cmd.Parameters.Add("username", SqlDbType.NVarChar).Value = "bob101";

Best/Correct way to call an Informix stored procedure via ADO.Net Command?

I have an Informix database which exposes some stored procedures, I have an abstracted data accessor that handles communicating with them but I have a problem with a null value.
Directly you can call:
execute procedure some_stored_procedure(1,2,NULL,3)
and get back correct results, I would rather there not be this nullable field, but it is out of my hands. Anyway I was originally trying to call it like so:
var command = connection.CreateCommand();
command.CommandType = CommandTypes.StoredProcedure
command.CommandText = "some_stored_procedure"
// Pass in the parameters
However doing that causes Informix to throw a syntax error, so instead I have been forced to go with the:
var command = connection.CreateCommand();
command.CommandText = "execute procedure some_stored_procedure(?,?,?,?)";
// Pass in parameters
Which works but never passes back correct results, and if I try and make parameter 3 null it gives another syntax error. Am I missing something or is there a better way to call these stored procedures?
try parameterizing the parameters (you can use OdbcParameters if working with the odbcdriver) and then pass DbNull.Value where Null is required.
Try this:
var command = connection.CreateCommand();
command.CommandType = CommandTypes.Text;
command.CommandText = "call some_stored_procedure(?,?,?,?)";
command.Parameters.Add(param); //add all your parameters.
Format the query as follow:
strQuery = string.Format("EXECUTE PROCEDURE Cronos_UpdateStateLegacyProduct ({0})", oValidityProducts.PurchaseId);
OdbcConnection oConnection = new OdbcConnection(this.strConnectionString);
OdbcCommand oCommand = new OdbcCommand();
oCommand.CommandType = CommandType.StoredProcedure;
oCommand.CommandText = strQuery;
oCommand.Connection = oConnection;
oConnection.Open();
intResult = oCommand.ExecuteNonQuery();
Best Regards
In case you are using an ODBC connection, when using CommandType.StoredProcedure you must use a diferent syntax for the procedure's name. In your case:
CommandText = "{ CALL some_stored_procedure(?,?,?,?)}"
Check this link for more information: https://support.microsoft.com/en-us/kb/310130

Execute an oracle Function that returns a reference cursor in C#

I have an oracle package with a procedure that has a in out reference cursor. My understanding is that this is pretty standard.
What I didn't like is the fact that I had to write a ton of code to just see the output. So I asked this question and it turns out I can get what I want by creating a function that wraps the procedure.
Update: Looks like I don't need the function anymore but it may be worth knowing anyway for those curious see the original question and answer updates.
Here's the function
FUNCTION GetQuestionsForPrint (user in varchar2)
RETURN MYPACKAGE.refcur_question
AS
OUTPUT MYPACKAGE.refcur_question;
BEGIN
MYPACKAGE.GETQUESTIONS(p_OUTPUT => OUTPUT,
p_USER=> USER ) ;
RETURN OUTPUT;
END;
and here's what I do to execute it in SQL Developer
var r refcursor;
exec :r := mypackage.getquestionsForPrint('OMG Ponies');
print r;
So from now on I'm probably going to add the ForPrint functions to all my procedures.
This got me thinking, maybe functions are what I want and I don't need procedures.
To test this I tried executing the function from .NET, except I can't do it. Is this really the way it is.
using (OracleConnection cnn = new OracleConnection("Data Source=Test;User Id=Test;Password=Test;"))
{
cnn.Open();
OracleCommand cmd = new OracleCommand("mypackage.getquestionsForPrint");
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.Add ( "p_USER", "OMG Ponies");
cmd.Connection = cnn;
OracleDataReader rdr = cmd.ExecuteReader();
while (rdr.Read())
{
Console.WriteLine(rdr.GetOracleValue(0));
}
Console.ReadLine();
}
So I get the error.
getquestionsForPrint is not a procedure or is undefined
I tried ExecuteScalar as well with the same result.
EDIT Taking Slider345's advice I've also tried setting the command type to text and using the following statement and I get
invalid SQL statement
mypackage.getquestionsForPrint('OMG Poinies');
and
var r refcursor; exec :r := mypackage.getquestionsForPrint('OMG Poinies');
Using Abhi's variation for the command text
select mypackage.getquestionsForPrint('OMG Poinies') from dual
resulted in
The instruction at "0x61c4aca5"
referenced memory at "0x00000ce1". The
memory could not be "read".
Am I just barking up the wrong tree?
Update
Attempting to add an output parameter doesn't help.
cmd.Parameters.Add(null, OracleDbType.RefCursor, ParameterDirection.Output);
Not sure what the name should be since its the return value of a function (I've tried null, empty string, mypackage.getquestionsForPrint) but in all cases it just results in
ORA-06550: line 1, column 7:
PLS-00306: wrong number or types of
arguments in call to
'getquestionsForPrint'
Final Edit (hopefully)
Apparently Guddie asked a similar question 3 months after I did. He got the answer which is to
Set your command text to an anonymous block
Bind a parameter to the ref cursor setting the direction to output
Call Execute non reader.
Then use your parameter
using (OracleConnection cnn = new OracleConnection("Data Source=Test;User Id=Test;Password=Test;"))
{
cnn.Open();
OracleCommand cmd = new OracleCommand("mypackage.getquestionsForPrint");
cmd.CommandType = CommandType.Text;
cmd.CommandText = "begin " +
" :refcursor1 := mypackage.getquestionsForPrint('OMG Ponies') ;" +
"end;";
cmd.Connection = cnn;
OracleDataAdapter da = new OracleDataAdapter(cmd);
cmd.ExecuteNonQuery();
Oracle.DataAccess.Types.OracleRefCursor t = (Oracle.DataAccess.Types.OracleRefCursor)cmd.Parameters[0].Value;
OracleDataReader rdr = t.GetDataReader();
while(rdr.Read())
Console.WriteLine(rdr.GetOracleValue(0));
Console.ReadLine();
}
I have not tested this with a function, but for my stored procedures. I specify the out parameter for the refCursor.
command.Parameters.Add(new OracleParameter("refcur_questions", OracleDbType.RefCursor, ParameterDirection.Output));
If you are able to get the function to work with the CommandType.Text. I wonder if you can try adding the parameter above except with the direction as:
ParameterDirection.ReturnValue
I am using Oracle.DataAccess version 2.111.6.0
I had to go up and down between the question and answers to figure out the full code that works. So I am giving the full code here that worked for me for others -
var sql = #"BEGIN :refcursor1 := mypackage.myfunction(:param1) ; end;";
using(OracleConnection con = new OracleConnection("<connection string>"))
using(OracleCommand com = new OracleCommand())
{
com.Connection = con;
con.Open();
com.Parameters.Add(":refcursor1", OracleDbType.RefCursor, ParameterDirection.Output);
com.Parameters.Add(":param1", "param");
com.CommandText = sql;
com.CommandType = CommandType.Text;
com.ExecuteNonQuery();
OracleRefCursor curr = (OracleRefCursor)com.Parameters[0].Value;
using(OracleDataReader dr = curr.GetDataReader())
{
if(dr.Read())
{
var value1 = dr.GetString(0);
var value2 = dr.GetString(1);
}
}
}
Hope it helps.
I know this is quite an old post, but since it took me so long to figure out all of the minutia involved in getting .NET to "fight nice" with Oracle, I figured I'd put this advice out there for anyone else in this sticky situation.
I frequently call Oracle stored procedures that return a REF_CURSOR in our environment (.NET 3.5 against Oracle 11g). For a function, you can indeed name the parameter anything you'd like, but then you need to set its System.Data.ParameterDirection = ParameterDirection.ReturnValue then ExecuteNonQuery against the OracleCommand object. At that point the value of that parameter will be the ref_cursor that the Oracle function returned. Just cast the value as an OracleDataReader and loop through the OracleDataReader.
I'd post the full code, but I wrote the data access layer in VB.NET years ago, and the bulk of the code consuming the data access layer (our corporate intranet) is in C#. I figured mixing languages in a single response would be the larger faux pas.

Categories