Upsert Return ID - c#

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

Related

Stored Procedure Not Returning Result from C# Code, but It Returns Value when Executed as Query

I have written stored procedure to get customer id with given inputs.
When executed manually it returns value but not returning any value from
C# code.
create procedure getCustomerID
#firstname CHAR(25),
#lastname CHAR(25),
#middlename as CHAR(25)=null,
#DOB datetime,
#CustomerState as CHAR(25)=null,
#CustomerNumber INTEGER,
#ID nvarchar(25) output
as
begin
....
...
set #ID='something'
end
USE [TestDB]
GO
declare #ID nvarchar(25)
EXECute [dbo].[getCustomerID]
'A', 'B','C','1963-09-06','', 12345, #ID out
print 'ID:'+#ID
GO
OUTPUT
ID: CN0075
C# code:
try
{
using (SqlConnection conn = new SqlConnection("Connectionstring"))
{
if (conn.State == ConnectionState.Closed)
{
conn.Open();
}
using (var command = new SqlCommand("getCustomerID", conn)
{
CommandType = CommandType.StoredProcedure
})
{
command.Parameters.Add("#firstname", SqlDbType.Char, 25).Value = "A";
command.Parameters.Add("#lastname", SqlDbType.Char, 25).Value = "B";
command.Parameters.Add("#middlename", SqlDbType.Char, 25).Value = "C";
command.Parameters.Add("#CustomerState", SqlDbType.Char, 25).Value = "";
command.Parameters.Add("#DOB", SqlDbType.DateTime).Value = "1963-09-06";
command.Parameters.Add("#CustomerNumber", SqlDbType.Int).Value = "12345";
command.Parameters.Add("#ID", SqlDbType.NVarChar, 25).Direction =
ParameterDirection.Output;
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
string retunID = command.Parameters["#ID"].Value.ToString();
}
}
}
}
}
catch (Exception e)
{
Logger.Error(e.Message);
}
It does not throw exception. It executes the the stored procedure and does
not go inside While(reader.read()) loop and read data.
in your sp use select instate of print
create procedure getCustomerID
#firstname CHAR(25),
#lastname CHAR(25),
#middlename as CHAR(25)=null,
#DOB datetime,
#CustomerState as CHAR(25)=null,
#CustomerNumber INTEGER,
#ID nvarchar(25) output
as
begin
....
...
set #ID='something'
select #ID
end
USE [TestDB]
GO
declare #ID nvarchar(25)
EXECute [dbo].[getCustomerID]
'A', 'B','C','1963-09-06','', 12345, #ID out
select 'ID:'+#ID
GO
enter image description here

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

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.

Call to stored procedure returning 1 of 30 characters

I am programmatically calling a SQL Server stored procedure which should return 30 random characters. Instead, it is only returning 1 char. What am I missing?
The stored procedure is working as it should when I execute it in SQL Server, but in the C#, its not working correctly.
var messageId = "";
try
{
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["conn"].ConnectionString))
{
conn.Open();
using (SqlCommand command = new SqlCommand("GenerateMessageId", conn))
{
command.CommandType = CommandType.StoredProcedure;
command.Parameters.AddWithValue("#msgid", messageId);
command.Parameters[0].Direction = ParameterDirection.Output;
command.ExecuteNonQuery();
messageId = (string)command.Parameters[0].Value;
}
}
}
Stored procedure:
ALTER PROCEDURE [dbo].[GenerateMessageId]
#msgid varchar(30) OUT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
EXEC dbo.GenerateRandomString 1, 1, 1, null, 30, #msgid OUT
END
Try to add:
command.Parameters[0].Size = 30;
or
command.Parameters.Add("#msgid", SqlDbType.VarChar, 30).Direction = ParameterDirection.Output;
or use explicit declaration:
SqlParameter msgid= new SqlParameter {
ParameterName = "#msgid",
IsNullable = true,
Direction = ParameterDirection.Output,
DbType = DbType.String,
Size = 30,
Value = messageId,
} ;
command.Parameters.Add(msgid);

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;

how to pass ref variable to a SQL stored Procedure using ADO.NET?

i have that code using LINQ to call a stored procedure to save some data into database then return two variables from the stored procedure.
[ASP.NET code]
dbDataContext dbo = new dbDataContext();
dbo.AddNewDoctor(doctorName, email, password, ref DocId, ref result);
[SQL]
create PROCEDURE [dbo].[AddNewDoctor]
#doctorname nvarchar(100),
#email nvarchar(100),
#password nvarchar(MAX),
#docId int out,
#Result int out
AS
BEGIN
SET NOCOUNT ON;
declare #idCounter int
select #idCounter = count(*) from dbo.doctors
if EXISTS (select * from dbo.doctors where e_mail = #email)
begin
SET #Result = -1
set #docId= 0
end
else
begin
INSERT INTO [dbo].[doctors]
([doctor_id]
,[doctorname]
,[e_mail]
,[password]
VALUES
((#idCounter +1)
,#docotorname
,#email
,#password
)
SET #Result = 1
set #docId= (#idCounter + 1)
end
END
this code work very well what i want to do now to use ADO instead of LINQ, the problem with me is that i can't pass the ref variable as in LINQ so how can i do it using ADO
You'll have to do something like this. Use ParameterDirection
SqlParameter output = new SqlParameter(paramName, dbType);
output.Direction = ParameterDirection.Output;
command.Parameters.Add(output);
In your case you've to use SqlDbType.Int. Use Value property to read return value.
SqlParameter output = new SqlParameter(paramName, SqlDbType.Int);
output.Direction = ParameterDirection.Output;
command.Parameters.Add(output);
int Result = (int) output.Value; or int? Result = (int?) output.Value;
Try this
using (SqlConnection con = new SqlConnection("Your connection string"))
{
con.Open();
SqlCommand mycommand = new SqlCommand();
mycommand.Connection = con;
mycommand.CommandText = "dbo.AddNewDoctor";
mycommand.CommandType = CommandType.StoredProcedure;
mycommand.Parameters.AddWithValue(doctorName);
mycommand.Parameters.AddWithValue(email);
mycommand.Parameters.AddWithValue(password);
mycommand.Parameters.AddWithValue(ref DocId);
mycommand.Parameters.AddWithValue(ref result);
mycommand.ExecuteNonQuery();
}
Hope this helps thanks.
Refer to this article, there is an working example:
http://csharp-guide.blogspot.de/2012/05/linq-to-sql-call-stored-procedure-with_25.html

Categories

Resources