Execute a pl/sql function with OracleCommand - c#

i have this pl/sql function, the only thing it does is validate that the user exist in the database, if the user exists this returns "Y" but if the user dont exist this return "N", what I want is get the value that I return in pl/sql in c #.
I am using oracle 10g
CREATE OR REPLACE FUNCTION KRIST.f_Login (userName IN VARCHAR2,
password IN VARCHAR2)
RETURN VARCHAR2
IS
CURSOR USERFINDER IS
SELECT IdEmpleado
FROM EMPLEADO
WHERE Usuario=userName
AND Clave=password;
id number;
returnVal VARCHAR2(1);
BEGIN
OPEN USERFINDER;
FETCH USERFINDER INTO id;
IF(id IS NULL) THEN
returnVal:='Y';
RETURN returnVal;
END IF;
returnVal:='N';
RETURN returnVal;
CLOSE USERFINDER;
END;
/
how I can perform this function and get the result in a variable... i have thos code but dont works
OracleCommand cmd = new OracleCommand("krist.p_login",conn);
cmd.CommandType = CommandType.StoredProcedure; // use StoredProcedure with Functions as well
OracleParameter returnVal = new OracleParameter("returnVal",null);
OracleParameter p_one = new OracleParameter("userName","kristian");
OracleParameter p_two = new OracleParameter("password", "kristian");
returnVal.OracleType = OracleType.VarChar;
returnVal.Size = 1;
p_one.OracleType = OracleType.VarChar;
p_two.OracleType = OracleType.VarChar;
p_one.DbType = DbType.String;
p_two.DbType = DbType.String;
returnVal.DbType = DbType.String;
returnVal.Direction = ParameterDirection.ReturnValue;
p_one.Direction = ParameterDirection.Input;
p_two.Direction = ParameterDirection.Input;
cmd.Parameters.Add(p_one);
cmd.Parameters.Add(p_two);
cmd.Parameters.Add(returnVal);
cmd.ExecuteNonQuery();
String bval = Convert.ToString(returnVal.Value);
return bval;

The following code works for me.
NB: Your pl/sql code called the function KRIST.f_Login, but your c# called it krist.p_login
NB2: Your pl/sql code used Varchar2, but your c# used varchar
NB3: I am using Oracle.DataAccess.dll
NB4: I assume your return value buffer size could be 1, but try different sizes.
using Oracle.DataAccess.Client;
using Oracle.DataAccess.Types;
int RETURN_VALUE_BUFFER_SIZE = 32767;
OracleCommand cmd = new OracleCommand();
try {
cmd.Connection = conn;
cmd.CommandText = "KRIST.f_Login";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("returnVal", OracleDbType.Varchar2, RETURN_VALUE_BUFFER_SIZE);
cmd.Parameters["returnVal"].Direction = ParameterDirection.ReturnValue;
cmd.Parameters.Add("userName", OracleDbType.Varchar2);
cmd.Parameters["userName"].Value = "kristian";
cmd.Parameters.Add("password", OracleDbType.Varchar2);
cmd.Parameters["password"].Value = "kristian";
cmd.ExecuteNonQuery();
string bval = cmd.Parameters["returnVal"].Value.ToString();
return bval;
} catch (Exception e) {
// deal with exception
} finally {
command.Dispose();
connection.Close();
connection.Dispose();
}

As far as I remember If you are using ODP.NET you need to provide retVal parameter as first.
Something is wrong with ODP.NET and it dosn't bind parameters with provided parameter names but with order of parameters.
So simply change order to:
cmd.Parameters.Add(returnVal);
cmd.Parameters.Add(p_one);
cmd.Parameters.Add(p_two);
And in my sources I found that return parameter i called "RETURN" (not sure if it counts):
OracleParameter returnVal = new OracleParameter("RETURN",null);
A ha and one more thing. It will never reach last line - cuase return would terminate execute. Close it as soon as you don't need it anymore.
RETURN returnVal;
CLOSE USERFINDER; --<<-- won't close this cursor

ODP.net binds by order by default. This behavior can be modified with:
cmd.BindByName = true

Related

Oracle VARCHAR Parameter always returns "1" from PL/SQL Stored Procedure in C#

I am trying to get a string from a stored procedure in C# .NET from Oracle PL/SQL. The query is OK in Oracle SQL Developer, but the output parameter in C# will always return "1". I have tried setting different parameter size, direction, etc. Nothing helps. It will either return "1"/null or throw an error. Also I added an INT32 param just for testing, and it will return null every time. Above is a simplified testing query and the C# code. Being struggling for two days now on this silly thing.
string sql = #"
DECLARE
eligProdFinal1 nvarchar2(128);
status integer;
testNR integer;
BEGIN
--dbms_output.put_line( eligProdFinal1 || '|' || eligProdFinal2 || '|' || eligProdFinal3 );
dbms_output.put_line('TEST');
eligProdFinal1 := '';
dbms_output.get_line( :eligProdFinal1, :status ); --status 0 is OK
dbms_output.put_line(37);
dbms_output.get_line( :testNR, :status ); --status 0 is OK
END;
";
.NET:
//added to get DBMS Ouput Line from a query in PL/SQL ORACLE
public static string GetDbmsOutputLine(string sqlExp)
{
string dbConSAPCCDEV = "...";
using (var connection = new OracleConnection() { ConnectionString = dbConSAPCCDEV })
{
using (OracleCommand command = new OracleCommand())
{
command.Connection = connection;
command.CommandType = System.Data.CommandType.Text;
command.CommandText = string.Format(sqlExp);
connection.Open();
OracleParameter statusParameter = new OracleParameter();
statusParameter.ParameterName = "status";
statusParameter.Direction = ParameterDirection.Output;
command.Parameters.Add(statusParameter);
//tried adding the param like this, or in a single line like below
//OracleParameter lineParameter = new OracleParameter();
//lineParameter.ParameterName = "eligProdFinal1";
//lineParameter.OracleDbType = OracleDbType.Varchar2;
//lineParameter.Size = 760;
//lineParameter.Direction = ParameterDirection.ReturnValue;
//command.Parameters.Add(lineParameter);
command.Parameters.Add(new OracleParameter("eligProdFinal1", OracleDbType.NVarchar2, 128, null, ParameterDirection.Output));
OracleParameter testParameter = new OracleParameter();
testParameter.ParameterName = "testNR";
testParameter.Direction = ParameterDirection.Output;
command.Parameters.Add(testParameter);
command.ExecuteNonQuery();
if (command.Parameters["eligProdFinal1"].Value is DBNull)
return null;
string output = command.Parameters["eligProdFinal1"].Value.ToString();
string testNr = command.Parameters["testNR"].Value.ToString();
connection.Close();
return output;
}
}
}

Error: "The SqlParameter is already contained by another SqlParameterCollection" when adding a Parameter

I have a few similar functions that interact with my SQL server (selects, calls to stored procdures etc.) and all work with the exception of the one below. Each SqlConnection is contained within a using block with the SqlCommand also contained within a using block.
This code is failing when attempting to add the #LastUpdated parameter. I've tried some suggestions I've seen in other posts: cmd.Parameters.Clear(), wrapping in using, etc., but no luck. A few posts with the same error were resolved when duplicate attempts to set the same parameter where found. It's possible I'm missing that but I've looked over this for a few hours, even cleaned the glasses. Any direction would be appreciated.
private void _AddCartItem(bool hasEventInCart, _EventCart cartContents, ref int cartItemId)
{
using (SqlConnection sqlConnection = new SqlConnection(_striMISConnection))
{
sqlConnection.Open();
using (SqlCommand cmd = sqlConnection.CreateCommand())
{
SqlParameter param = new SqlParameter();
cmd.Parameters.Clear();
// add/update CartItem
if (hasEventInCart)
{
// Update item
cmd.CommandText = "SProc2Insert #CartItemId, #ID, 'Event', #Created, #LastUpdated";
param.ParameterName = "#CartItemId";
param.Value = cartItemId;
cmd.Parameters.Add(param);
}
else
{
// add item
cmd.CommandText = "SProc2Update #ID, 'Event', #Created, #LastUpdated";
}
cmd.CommandType = CommandType.Text;
param.ParameterName = "#Created";
param.Value = DateTime.Now.ToString();
cmd.Parameters.Add(param);
param.ParameterName = "#LastUpdated";
param.Value = DateTime.Now.ToString();
**cmd.Parameters.Add(param);**
param.ParameterName = "#ID";
param.Value = this.ID;
cmd.Parameters.Add(param);
if (hasEventInCart)
{
cmd.ExecuteNonQuery(); // do the update
}
else
{
cartItemId = (int)cmd.ExecuteScalar();
foreach (var currentCartEvent in cartContents.CartEvents)
{
if (currentCartEvent.EventCode == this.EventCode)
{
currentCartEvent.CartItemID = cartItemId;
}
}
}
cmd.Parameters.Clear();
}
}
}
I had the same problem, you can solve it with the code below:
if (SqlParams != null && SqlParams.Count > 0)
{
foreach (SqlParameter spp in SqlParams )
{
SqlParameter nameParam = new SqlParameter(spp.ParameterName, spp.SqlValue);
mycmd.Parameters.Add(nameParam);
}
}
First of all, if you're calling a stored procedure, you need to set your CommandType to CommandType.StoredProcedure - not Text :
cmd.CommandType = CommandType.StoredProcedure;
and just use the stored procedure name as your command query:
cmd.CommandText = "dbo.SProc2Update";
Second, I wouldn't use just a single SqlParameter instance and keep adding that over and over again - I would just use the .Add() method directly - and stop converting all date/time to string! Use the appropriate native datatype they have - DateTime - and specify as such in your Parameters.Add() call:
cmd.Parameters.Add("#Created", SqlDbType.DateTime).Value = DateTime.Now;
cmd.Parameters.Add("#LastUpdated", SqlDbType.DateTime).Value = DateTime.Now;
This is how I got this working:
ILease lease = (ILease)_SqlParameterCollection.InitializeLifetimeService();
if (lease.CurrentState == LeaseState.Initial)
{
lease.InitialLeaseTime = TimeSpan.FromMinutes(5);
lease.SponsorshipTimeout = TimeSpan.FromMinutes(2);
lease.RenewOnCallTime = TimeSpan.FromMinutes(2);
lease.Renew(new TimeSpan(0, 5, 0));

Can't get it! SqlParameter with ParameterName is not contained by this SqlParameterCollection. Stored proc param

I know that this question has been asked many times, And I have read many many answers, yet I can't figure out what is wrong. It's been hours. Any help would be soo appreciated. I am just trying to call a stored procedure in an ASP page, and I am unable to add the parameter properly, getting the exception that it is not in the collection.
I have modified my proc as follows to try to make it simple and isolate the issue.
ALTER PROCEDURE [dbo].[up_validateUserWithClinicCount]
#HTID INT = 0,
#ValidUserID INT OUTPUT,
#MultiClinicFlag INT OUTPUT
AS
DECLARE #vClinicCount INT = null
DECLARE #vUserValid INT = null
BEGIN
SET #ValidUserID = 2
SET #MultiClinicFlag = 1
END;
AND the C# code
String connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["PC3PaymentConnection"].ConnectionString;
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
using (SqlCommand cmd = new SqlCommand("up_validateUserWithClinicCount", connection))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#HTID", htId);
SqlParameter uidOut = new SqlParameter("#ValidUserID", SqlDbType.Int);
uidOut.Size = 4;
uidOut.Direction = ParameterDirection.Output;
cmd.Parameters.Add(uidOut);
SqlParameter pMultiClinics = new SqlParameter();
pMultiClinics.ParameterName = "#MultiClinicFlag";
pMultiClinics.SqlDbType = SqlDbType.Int;
pMultiClinics.Direction = ParameterDirection.Output;
cmd.Parameters.Add(pMultiClinics);
try
{
cmd.ExecuteNonQuery();
//--> Error points to the next line, and I have tried to use int.parse rather than convert also, with the same error -- parameter not in collection
MultiClinics = Convert.ToInt16(cmd.Parameters["pMultiClinics"].Value);
PC3User = Convert.ToInt16(uidOut.Value.ToString());
}
catch (SqlException sqlEx)
{
LbMsg.ForeColor = System.Drawing.Color.Red;
LbMsg.Text = sqlEx.Message;
}
}
}
Thanks if you can see what I am missing.
You have an object reference for the parameter already, you don't need to grab it from the parameters collection. Also, sql ints are 32-bit.
MultiClinics = (int)pMultiClinics.Value;
To retrieve from the parameter collection, use the ParameterName you gave it:
MultiClinics = (int)cmd.Parameters["#MultiClinicFlag"].Value;

Calling Stored procedure with output

I have been trying to retrieve some information from my database, and also retrieve the return value. I know the Stored Procedure works fine.
The code I use is a modified piece I use for registering the user. It's going wrong at the cmd.ExecuteReader part of my code.
protected void btn_login_Click(object sender, ImageClickEventArgs e)
{
//Actions after Submit button is clicked
Page.Validate(((ImageButton)sender).ValidationGroup);
if (Page.IsValid)
{
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["DbConnectString"].ConnectionString))
{
SqlCommand cmd = new SqlCommand("usp_validateUsers", conn);
//Input Values
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("username", Uname.Text);
cmd.Parameters.AddWithValue("password", pwd.Text);
//Return Values
SqlParameter retParam = cmd.Parameters.Add("#RetVal", SqlDbType.Int);
retParam.Direction = ParameterDirection.ReturnValue;
SqlParameter acsParam = cmd.Parameters.Add("#ac_status", SqlDbType.Int);
acsParam.Direction = ParameterDirection.Output;
SqlParameter nikParam = cmd.Parameters.Add("#memb_name", SqlDbType.VarChar);
nikParam.Direction = ParameterDirection.Output;
try
{
// Open Connection and execute Stored Proc
conn.Open();
///////////SOMETHING GOES WRONG HERE///////////////
cmd.ExecuteReader();
//Retrieve Data
int retVal = (int)retParam.Value;
string nickname = nikParam.Value.ToString();
string ac_stats = acsParam.Value.ToString();
if (retVal != 0)
{
//Invalid Username or password
}
else
{
//Login User
}
}
catch (Exception Error)
{
lbl_login.Text = "An error occured, please try again later";
debug.Text = Error.Message;
}
finally
{
debug.Text = "\n Clossing Connection";
if (conn.State == System.Data.ConnectionState.Open)
{
conn.Close();
}
}
}
}
}
When I just want to receive the return value I simply use cmd.ExecuteScalar(); I know how to receive data when I'm passing the SQL query to the SQL database, but it seems to be different when using Stored Procedures..
EDIT
Probably could improve this code further but it really does what it should do.
ALTER PROCEDURE dbo.usp_validateUsers
#username varchar(10),
#password varchar(10),
#ac_status char(1) OUTPUT,
#memb_name varchar(15) OUTPUT
AS
IF EXISTS(SELECT * FROM MEMB_INFO WHERE (memb___id = #username))
BEGIN
SELECT #ac_status = ac_status, #memb_name = memb_name
FROM MEMB_INFO
WHERE (memb___id = #username) AND (memb__pwd = #password)
RETURN 0
END
ELSE
BEGIN
return 1
END
When I use break points to catch possible exceptions in Visual Studio, It gives me:
String[4]: The Size property has an invalid size of 0
The error you mentioned may be caused by the fact that you're not specifying the size of your VarChar parameters. Instead of having lines like this:
SqlParameter nikParam = cmd.Parameters.Add("#memb_name", SqlDbType.VarChar);
Try this:
SqlParameter nikParam = cmd.Parameters.Add("#memb_name", SqlDbType.VarChar, 15);
You need to create a SqlDataReader.
From Ins and Outs of using Stored Procedures in C#
The SqlDataReader class is used to
read a forward only stream of records
returned from the database. The
SqlDataReader object is not
instantiated directly through a
constructor (hence the lack of the New
key word) but rather through the
ExecuteReader method of the SqlCommand
object. Before calling the
ExecuteReader method the connection to
the database is established using the
Open method of the SqlConnection
object.
Try
SqlDataReader drLogins;
Conn.Open();
drLogins = cmd.ExecuteReader();
Your #ac_status is defined as integer in parameter. change it character or string.
SqlParameter acsParam = cmd.Parameters.Add("#ac_status", SqlDbType.Int);

How Run A Stored Procedure (with Parameters - Has A return Value) From Code Behind?

How can I use a stored procedure (with parameters - has a return value of type int) from code behind?
My stored procedure looks like this :
ALTER Procedure [dbo].[sp_Noskheh_SumOfTotalPay]
#Co_ID int
AS
-----------------
Declare #Sum bigint
-----------------
BEGIN
SELECT
#Sum = SUM(TotalPay)
FROM Noskheh
WHERE
(Co_ID = #Co_ID)
RETURN #Sum
END
I want to use #Sum in code behind ...
Would you please show me a way for doing that ?
Thanks in advance
best regards
You need to set up a SqlConnection and a SqlCommand. If you have your code with the RETURN #Sum statement in the end, you need to do this (define a parameter of type RETURN_VALUE):
using(SqlConnection _conn = new SqlConnection(-your-connection-string-here))
using(SqlCommand _cmd = new SqlCommand("dbo.sp_Noskheh_SumOfTotalPay", _conn))
{
_cmd.CommandType = CommandType.StoredProcedure;
_cmd.Parameters.Add(new SqlParameter("#CO_ID", SqlDbType.Int));
_cmd.Parameters["#CO_ID"].Value = 5; // whatever value you want
_cmd.Parameters.Add(new SqlParameter("#RETURN_VALUE", SqlDbType.BigInt));
_cmd.Parameters["#RETURN_VALUE"].Direction = ParameterDirection.ReturnValue;
_conn.Open();
_cmd.ExecuteNonQuery();
Int64 result = Int64.Parse(_cmd.Parameters["#RETURN_VALUE"].Value);
_conn.Close();
}
It would be a lot easier if you would replace that RETURN statement with a simple SELECT:
SELECT #Sum
In that case, you can use the simplified version I had before - using .ExecuteScalar() to retrieve the single value of the single row being returned from the stored proc:
using(SqlConnection _conn = new SqlConnection(-your-connection-string-here))
using(SqlCommand _cmd = new SqlCommand("dbo.sp_Noskheh_SumOfTotalPay", _conn))
{
_cmd.CommandType = CommandType.StoredProcedure;
_cmd.Parameters.Add(new SqlParameter("#CO_ID", SqlDbType.Int));
_cmd.Parameters["#CO_ID"].Value = 5; // whatever value you want
_conn.Open();
object result = _cmd.ExecuteScalar();
_conn.Close();
Int64 sum = Int64.Parse(result);
}
That should call your stored proc, read the single value you're returning, and converting it into an int variable called sum.
There is no shortage of tutorials on this subject.
You can add an SqlParameter #Sum with Direction set to ReturnValue
Example:
SqlCommand cmd = new SqlCommand():
cmd.Connection = // place your SqlConnection object;
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "StoreProcedureName";
cmd.Parameters.Add("#Sum", SqlDbType.BigInt).Direction = ParameterDirection.ReturnValue

Categories