Integer out parameter null issue in ADO.NET while calling through procedure - c#

I'm facing one issue when I try to get output value from SQL Server stored procedure which is of INT type, then I'm getting NULL value ☹ .
Here is the stored procedure:
CREATE PROCEDURE dbo.NULLISSUE
#input VARCHAR(10),
#output INT = 0 OUTPUT
AS
BEGIN
IF #input >= '1'
BEGIN
SET #output = #output + 1
RETURN(0)
END
END
Here is the .NET code:
using (var connection = new SqlConnection(connectionString))
{
using (var command = new SqlCommand())
{
command.Connection = connection;
command.CommandType = CommandType.StoredProcedure;
command.CommandText = "dbo.NULLISSUE";
command.Parameters.Clear();
command.Parameters.AddWithValue("#input", "1");
command.Parameters.AddWithValue("#output", SqlDbType.Int);
command.Parameters["#output"].Value = 0;
command.Parameters["#output"].Direction = ParameterDirection.Output;
command.CommandTimeout = 3000;
connection.Open();
command.ExecuteNonQuery();
connection.Close();
Console.WriteLine(command.Parameters["#output"].Value);
//null value ?
Console.ReadLine();
}
}
I have a fix by doing ISNULL(#output, 0) in the procedure but not sure why it can be done on the .NET side and why ADO.NET not passing/setting/initializing OUTPUT parameter = 0.
Thanks in advance.

#output must be ParameterDirection.InputOutput if you want it to use the initial value set within the client.
Currently as it's ParameterDirection.Output the value is ignored, it defaults to NULL within the procedure and NULL + anything results in a NULL.

Related

SQL Out parameter received as null while passing from C#

I am trying to populate a value in an out parameter but it is returned as null from the stored procedure.
But it works fine when I try to run the query through SSMS.
Stored procedure:
CREATE PROCEDURE [dbo].[USP_ABCD]
(#signal_xml AS NVARCHAR(MAX) OUT) -- this parameter includes the result in case
AS
BEGIN
DECLARE #signal_data XML
SET #signal_data = cast(#signal_xml as XML)
INSERT INTO DBLog
VALUES ('USP_ABCD', 'test', #signal_xml, ERROR_MESSAGE(), GETUTCDATE())
SET #signal_xml = 'ABCD';
END
C# code calling this stored procedure:
private static void runSP()
{
try
{
var temp = File.ReadAllText(#"abcd\TEST1.txt");
var conStr = System.Configuration.ConfigurationManager.ConnectionStrings["test"].ConnectionString;
using (SqlConnection connection = new SqlConnection(conStr))
{
connection.Open();
SqlCommand command = connection.CreateCommand();
// Setup the command to execute the stored procedure.
command.CommandText = "dbo.USP_ABCD";
command.CommandType = CommandType.StoredProcedure;
// Set up the output parameter to retrieve the summary.
SqlParameter paramSignalXml = new SqlParameter("#signal_xml", SqlDbType.NVarChar, -1);
paramSignalXml.Direction = ParameterDirection.Output;
paramSignalXml.Value = temp;
command.Parameters.Add(paramSignalXml);
// Execute the stored procedure.
command.ExecuteNonQuery();
Console.WriteLine((String)(paramSignalXml.Value));
var retValue = (String)(paramSignalXml.Value);
connection.Close();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
Is it not possible to pass value to output parameter or am I missing something? Can someone help me with this? Thanks in advance.

A SqlParameter with parameter name '#name' is not contained by this SqlParameterCollection function

I am trying to return a value from the code below but I am getting an error that says:
A SqlParameter with parameter name '#vRESULT' is not contained by this SqlParameterCollection
c# Code:
public int userLogin()
{
string connStr = ConfigurationManager.ConnectionStrings["conn"].ToString();
string cmdStr = #"fucn_LOg";
using (SqlConnection conn = new SqlConnection(connStr))
using (SqlCommand cmd = new SqlCommand(cmdStr, conn))
{
try
{
conn.Open();
cmd.CommandText = cmdStr;
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Clear();
cmd.Parameters[":vResult"].Direction = ParameterDirection.Output;
cmd.Parameters.Add(new SqlParameter("param1", SqlDbType.VarChar)).Value = TB_1.Text;
cmd.Parameters.Add(new SqlParameter("param2", SqlDbType.VarChar)).Value = TB_2.Text;
cmd.ExecuteScalar();
return Int32.Parse(cmd.Parameters[":vResult"].Value.ToString());
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
return -1;
}
}
}
the sql server function code below with returning parameter DECLARE #vResult int
CREATE FUNCTION USER_LOGIN(#USER_NAME VARCHAR(60),
#PWD VARCHAR(60))
RETURNS INT
AS BEGIN
DECLARE #vResult int
SELECT #vRESULT=COUNT(*)
FROM OPER
WHERE UPPER(UNAM)=UPPER(#USER_NAME)
AND PSW=#PWD
IF #vResult=1
SET #vResult=1
ELSE
SET #vResult= -1
RETURN #vResult
END
Just Get result from Stroed Procedure like this:
var result = cmd.ExecuteScalar();
return Int32.Parse(result.ToString());
This gets first and Only result from Stored Procedure.
Also recommend simplify your code Like this:
public int userLogin() {
string connStr = ConfigurationManager.ConnectionStrings["conn"].ToString();
using (SqlConnection conn = new SqlConnection(connStr))
using (SqlCommand cmd = new SqlCommand("fucn_LOg", conn)) {
try {
cmd.Connection.Open();
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#param1", TB_1.Text);
cmd.Parameters.AddWithValue("#param2", TB_2.Text);
var result = cmd.ExecuteScalar();
return Int32.Parse(result.ToString());
}
catch (Exception ex) {
MessageBox.Show(ex.ToString());
return -1;
}
finally {
if (cmd.Connection.State != ConnectionState.Closed) cmd.Connection.Close();
}
}
}
And your Stored procedure should looks Like this:
CREATE PROCEDURE fucn_LOg
(
#param1 nvarchar(max),
#param2 nvarchar(max)
)
AS
BEGIN
SET NOCOUNT ON;
if (exists(select * from tbUsers where flLogin = #param1 and flPassword = #param2))
begin
return 1;
end
else
begin
return 0;
end
END
GO
OR
CREATE PROCEDURE fucn_LOg
(
#param1 nvarchar(max),
#param2 nvarchar(max)
)
AS
BEGIN
SET NOCOUNT ON;
select COUNT(*) from tbUsers where flLogin = #param1 and flPassword = #param2
END
GO
Several problems.
First, you don't need the cmd.Parameters.Clear();, as you just establish a new cmd.
Second, use # for SQL Server parameters.
Third, a parameter named vResult is not set, so cmd.Parameters[":vResult"].Direction is invalid. You need to assign its type and value. Make sure your stored procedure has this parameter set with correct SQL data type.
Lastly, I guess you return the vResult in your stored procedure like select #vResult; so make it a new vResult = function(vResult). But no, it is not how SQL Server work. It won't change your input parameter even though you return your #vResult. While, ExecuteScaler does. So, simply get your result back by var result = cmd.ExecuteScalar();.
You are getting data from a stored procedure, not getting back the parameter you sent. That's the supposed correct way.
conn.Open();
cmd.CommandText = cmdStr;
cmd.CommandType = CommandType.StoredProcedure;
//Base on sql you provided, it is no need for this part.
/*
SqlParameter vResult = new SqlParameter();
vResult.ParameterName = "#vResult";
vResult.Direction = ParameterDirection.Output;
vResult.SqlDbType = System.Data.SqlDbType.???;
vResult.Value = ???;
cmd.Parameters.Add(vResult);
*/
cmd.Parameters.Add("#param1", SqlDbType.VarChar).Value = TB_1.Text;
cmd.Parameters.Add("#param2", SqlDbType.VarChar).Value = TB_2.Text;
var result = cmd.ExecuteScalar();
return Int32.Parse(result.ToString());
This is hard to debug without the SP, but a couple of things jump out.
First, you need to use the '#' character as a prefix for your parameter names, not a colon.
Second, you should define your output parameter like this:
SqlParameter outputParam = new SqlParameter("#vResult", SqlDbType.Int);
outputParam.Direction = ParameterDirection.Output;
cmd.Parameters.Add(outputParam);

ExecuteNonQuery with return value in a stored procedure [duplicate]

I am trying to call a stored procedure from my C# windows application. The stored procedure is running on a local instance of SQL Server 2008. I am able to call the stored procedure but I am not able to retrieve the value back from the stored procedure. This stored procedure is supposed to return the next number in the sequence. I have done research online and all the sites I've seen have pointed to this solution working.
Stored procedure code:
ALTER procedure [dbo].[usp_GetNewSeqVal]
#SeqName nvarchar(255)
as
begin
declare #NewSeqVal int
set NOCOUNT ON
update AllSequences
set #NewSeqVal = CurrVal = CurrVal+Incr
where SeqName = #SeqName
if ##rowcount = 0 begin
print 'Sequence does not exist'
return
end
return #NewSeqVal
end
Code calling the stored procedure:
SqlConnection conn = new SqlConnection(getConnectionString());
conn.Open();
SqlCommand cmd = new SqlCommand(parameterStatement.getQuery(), conn);
cmd.CommandType = CommandType.StoredProcedure;
SqlParameter param = new SqlParameter();
param = cmd.Parameters.Add("#SeqName", SqlDbType.NVarChar);
param.Direction = ParameterDirection.Input;
param.Value = "SeqName";
SqlDataReader reader = cmd.ExecuteReader();
I have also tried using a DataSet to retrieve the return value with the same result. What am I missing to get
the return value from my stored procedure? If more information is needed, please let me know.
You need to add a ReturnValue-direction parameter to the command:
using (SqlConnection conn = new SqlConnection(getConnectionString()))
using (SqlCommand cmd = conn.CreateCommand())
{
cmd.CommandText = parameterStatement.getQuery();
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("SeqName", "SeqNameValue");
// #ReturnVal could be any name
var returnParameter = cmd.Parameters.Add("#ReturnVal", SqlDbType.Int);
returnParameter.Direction = ParameterDirection.ReturnValue;
conn.Open();
cmd.ExecuteNonQuery();
var result = returnParameter.Value;
}
Setting the parameter's direction to ParameterDirection.ReturnValue instructs the SqlCommand to declare it as a variable and assign the stored procedure's return value to it (exec #ReturnValue = spMyProcedure...), exactly like you would write it in SQL.
I know this is old, but i stumbled on it with Google.
If you have a return value in your stored procedure say "Return 1" - not using output parameters.
You can do the following - "#RETURN_VALUE" is silently added to every command object. NO NEED TO EXPLICITLY ADD
cmd.ExecuteNonQuery();
rtn = (int)cmd.Parameters["#RETURN_VALUE"].Value;
The version of EnterpriseLibrary on my machine had other parameters.
This was working:
SqlParameter retval = new SqlParameter("#ReturnValue", System.Data.SqlDbType.Int);
retval.Direction = System.Data.ParameterDirection.ReturnValue;
cmd.Parameters.Add(retval);
db.ExecuteNonQuery(cmd);
object o = cmd.Parameters["#ReturnValue"].Value;
I had a similar problem with the SP call returning an error that an expected parameter was not included. My code was as follows.
Stored Procedure:
#Result int OUTPUT
And C#:
SqlParameter result = cmd.Parameters.Add(new SqlParameter("#Result", DbType.Int32));
result.Direction = ParameterDirection.ReturnValue;
In troubleshooting, I realized that the stored procedure was ACTUALLY looking for a direction of "InputOutput" so the following change fixed the problem.
r
Result.Direction = ParameterDirection.InputOutput;
This is a very short sample of returning a single value from a procedure:
SQL:
CREATE PROCEDURE [dbo].[MakeDouble] #InpVal int AS BEGIN
SELECT #InpVal * 2; RETURN 0;
END
C#-code:
int inpVal = 11;
string retVal = "?";
using (var sqlCon = new SqlConnection(
"Data Source = . ; Initial Catalog = SampleDb; Integrated Security = True;"))
{
sqlCon.Open();
retVal = new SqlCommand("Exec dbo.MakeDouble " + inpVal + ";",
sqlCon).ExecuteScalar().ToString();
sqlCon.Close();
}
Debug.Print(inpVal + " * 2 = " + retVal);
//> 11 * 2 = 22
ExecuteScalar(); will work, but an output parameter would be a superior solution.
You can try using an output parameter. http://msdn.microsoft.com/en-us/library/ms378108.aspx
Or if you're using EnterpriseLibrary rather than standard ADO.NET...
Database db = DatabaseFactory.CreateDatabase();
using (DbCommand cmd = db.GetStoredProcCommand("usp_GetNewSeqVal"))
{
db.AddInParameter(cmd, "SeqName", DbType.String, "SeqNameValue");
db.AddParameter(cmd, "RetVal", DbType.Int32, ParameterDirection.ReturnValue, null, DataRowVersion.Default, null);
db.ExecuteNonQuery(cmd);
var result = (int)cmd.Parameters["RetVal"].Value;
}
I see the other one is closed. So basically here's the rough of my code. I think you are missing the string cmd comment. For example if my store procedure is call:DBO.Test. I would need to write cmd="DBO.test". Then do command type equal to store procedure, and blah blah blah
Connection.open();
String cmd="DBO.test"; //the command
Sqlcommand mycommand;

How can I obtain a value from an output parameter while also providing a value as an input?

I am using my code in C# below
cn = new SqlConnection(connetionString);
using (SqlCommand cmd = new SqlCommand("sp_Get_Cur", cn))
{
cmd.CommandType = CommandType.StoredProcedure;
SqlParameter parm = new SqlParameter("#cur", SqlDbType.VarChar);
parm.Size = 3;
parm.Value = s_cur;
parm.Direction = ParameterDirection.Input;
cmd.Parameters.Add(parm);
SqlParameter parm2 = new SqlParameter("#val", SqlDbType.Decimal);
parm2.Direction = ParameterDirection.Output;
parm2.Value = val;
cmd.Parameters.Add(parm2);
cn.Open();
cmd.ExecuteNonQuery();
cn.Close();
return (decimal)cmd.Parameters["#val"].Value;
}
my SP is:
ALTER PROCEDURE sp_Get_Cur
#cur VARCHAR(3),
#val decimal(16,2) OUTPUT
AS
BEGIN
DECLARE #rate decimal(16,2)
SELECT #rate = rate FROM exchange_rate WHERE code = #cur
SET #val = #val / #rate
END
But I am getting the error: {"Specified cast is not valid."}
I wanted to get the result of a currency from my exchange rate table.
Is there a better way of getting results simplier than doing output parameter direction type?
TEST RESULTS in SQL:
DECLARE #val decimal(16,2)
SET #val = 5
EXECUTE sp_Get_Cur 'EUR', #val output
SELECT #val
Result = 6.76
in Code Watch, my cmd.Parameters["#val"].Value; = 5.00;
when executed it becomes {} or null?
If you specify the parameter as output only it will not pass any value into the stored procedure, hence inside the procedure you will be dividing with a null which returns a null.
Change the parameter direction like this:
parm2.Direction = ParameterDirection.InputOutput;

Upsert Return ID

I'm trying to get the inserted or updated id from a stored procedure, i tried 2 different ways
I'm not allowed to use If exists(select ....) for performance reason
1- using OUTPUT
#firstName varchar(30),
#Telephone int
AS
BEGIN transaction
UPDATE [PHONE_BOOK].[dbo].[USER]
SET TELEPHONE= #Telephone
output inserted.USERID
where FIRST_NAME = #firstName
if ##ROWCOUNT = 0
begin
INSERT INTO [PHONE_BOOK].[dbo].[USER]
([FIRST_NAME]
,[TELEPHONE])
output inserted.USERID -- or output SCOPE_IDENTITY()
VALUES
(#firstName
,#Telephone)
end
C# code
IList<SqlParameter> parameters = new List<SqlParameter>();
parameters.Add(new SqlParameter("#firstName", "Mike"));
parameters.Add(new SqlParameter("#Telephone", 9514256));
using (var sqlConnection = new SqlConnection(Config.ConnString))
{
sqlConnection.Open();
using (SqlCommand command = sqlConnection.CreateCommand())
{
command.CommandType = CommandType.StoredProcedure;
command.CommandText = "[dbo].[INSET_USER]";
command.UpdatedRowSource = UpdateRowSource.None;
foreach (SqlParameter oPar in parameters)
command.Parameters.Add(oPar);
object x = command.ExecuteScalar();
}
}
In case of update ==> x is Correct
In case of delete ==> x is null
2- using OUTPUT parameter
#firstName varchar(30),
#Telephone int,
#userId int output
AS
BEGIN transaction
UPDATE [PHONE_BOOK].[dbo].[USER]
SET TELEPHONE= #Telephone
where FIRST_NAME = #firstName
-- i can't find different way to set #userId than doing a select which is not acceptable
if ##ROWCOUNT = 0
begin
INSERT INTO [PHONE_BOOK].[dbo].[USER]
([FIRST_NAME]
,[TELEPHONE])
VALUES
(#firstName
,#Telephone)
set #userId = SCOPE_IDENTITY()
end
c# Code:
IList<SqlParameter> parameters = new List<SqlParameter>();
parameters.Add(new SqlParameter("#firstName", "Mike"));
parameters.Add(new SqlParameter("#Telephone", 9514256));
using (var sqlConnection = new SqlConnection(Config.ConnString))
{
sqlConnection.Open();
using (SqlCommand command = sqlConnection.CreateCommand())
{
command.CommandType = CommandType.StoredProcedure;
command.CommandText = "[dbo].[INSET_USER]";
command.UpdatedRowSource = UpdateRowSource.None;
foreach (SqlParameter oPar in parameters)
command.Parameters.Add(oPar);
command.Parameters.Add("#userId",0).Direction = ParameterDirection.Output;
command.ExecuteNonQuery();
var x = command.Parameters["#userId"].Value;
}
}
In case of update ==> x is -1
In case of delete ==> x is correct
how can i fix this? i perefere first option as i don't need to pass output parameter to the stored procedure
The reason you aren't getting your output value in this case is because you are *select*ing it rather than *return*ing it. You can pick it up from the first result set if you don't want to change your proc - but ExecuteScalar suppresses result sets, so you'll want to use ExecuteReader or something like that. However, there is a standard pattern for this and you aren't following it, so I'll post it just to be nice - I'm sure it's been covered elsewhere though.
Standard SP pattern -
create proc UpsertMyTable (
#parameter int
) AS
declare #result int
if exists (select 1 from MyTable where value = #parameter) begin
UPDATE HERE
end else begin
INSERT HERE
end
set #result = SCOPE_IDENTITY()
return #result
And then in your C# code, basically what you have in your example is correct except the way you've done the parameters is redundant and a performance problem...
using (var sqlConnection = new SqlConnection(Config.ConnString)) {
sqlConnection.Open();
using (SqlCommand command = sqlConnection.CreateCommand()) {
command.CommandType = CommandType.StoredProcedure;
command.CommandText = "[dbo].[PROC_UPSERT_SHARE]";
command.UpdatedRowSource = UpdateRowSource.None;
command.parameters.Add(new SqlParameter("#fullName", "dwfae"));
command.parameters.Add(new SqlParameter("#name", "aze"));
command.parameters.Add(new SqlParameter("#root", "aze"));
command.parameters.Add(new SqlParameter("#creationDate", new DateTime(2000, 10, 10)));
command.parameters.Add(new SqlParameter("#lastAccessDate", new DateTime(1999, 11, 11)));
command.parameters.Add(new SqlParameter("#lastWriteDate", new DateTime(1990,12,12)));
command.parameters.Add(new SqlParameter("#subShareCount", 20));
command.parameters.Add(new SqlParameter("#serverId", "serverx"));
object x = command.ExecuteScalar();
Console.WriteLine(x);
}
}
Something like that will be much faster. Please consider this type of thing even if you think the impact might be small.
Consider using RETURN or OUTPUT parameter along with ExecuteNonQuery(), it automatically populates values into the variables. ExecuteScalar() is not required.
http://blogs.msdn.com/b/spike/archive/2009/05/07/a-simple-example-on-how-to-get-return-and-out-parameter-values-using-ado-net.aspx

Categories

Resources