How to retrieve stored procedure return value with Dapper - sql-server

I have a stored procedure of this form:
CREATE PROCEDURE AddProduct
(#ProductID varchar(10),
#Name nvarchar(150)
)
AS
SET NOCOUNT ON;
IF EXISTS (SELECT TOP 1 ProductID FROM Products
WHERE ProductID = #ProductID)
RETURN -11
ELSE
BEGIN
INSERT INTO Products ([AgentID], [Name])
VALUES (#AgentID, #Name)
RETURN ##ERROR
END
I have this C# to call the stored procedure, but I can't seem to get a correct value form it:
var returnCode = cn.Query(
sql: "AddProduct",
param: new { #ProductID = prodId, #Name = name },
commandType: CommandType.StoredProcedure);
How can I ensure that the returnCode variable will contain the value returned from either the RETURN -11 or the RETURN ##ERROR lines?

To enable use of the RETURN statement in SQL the C# becomes...
var _params = new DynamicParameters{ #ProductID = prodId, #Name = name };
_params.Add(
name: "#RetVal",
dbType: DbType.Int32,
direction: ParameterDirection.ReturnValue
);
var returnCode = cn.Execute(
sql: "AddProduct",
param: _params,
commandType: CommandType.StoredProcedure);
return _params.Get<int>("#RetVal");
It's not the implementation I was hoping for, but it works.

I suggest you use SELECT to return the values from your stored procedure. Also you should specify a return type on the dapper call:
RETURN -11
becomes
SELECT -11
and the dapper call becomes:
var returnCode = cn.Query<int>(..
I have implemented this pattern several times and it works as expected.

Related

Bug in MVC Core 2 - Stored Procedures returns null

how do I report a bug to MVC Core2 team, the stored procedure Output Parameter returns null,
Here is my code
var p = new SqlParameter
{
ParameterName = "token",
DbType = System.Data.DbType.String,
Size = 100, Value = "Sarah",
Direction = System.Data.ParameterDirection.Output
};
var z = p.SqlValue;
var resp = await _context.Database.ExecuteSqlCommandAsync("exec dbo.yourstoredprocedure {0}, {1}","test", p);
var y = p.SqlValue;
var x = p.Value;
resp is always -1 and x and y are both null, the stored procedure is simply
create PROCEDURE [dbo].[yourstoredprocedure]
( #varone nvarchar(max),
#vartwo nvarchar(50) OUTPUT)
AS
insert into test (two, three) select #varone, #vartwo'
SET #vartwo = scope_identity()
return #vartwo;
Like I said in the comments, your SP is full of Syntax errors. It should probably look more like this:
CREATE PROCEDURE [dbo].[yourstoredprocedure] #varone nvarchar(max),
#vartwo nvarchar(50),
#varfour int OUTPUT AS --What happened to #varthree ?
INSERT INTO test (two, three)
VALUES (#varone, #vartwo);
SET #varfour = SCOPE_IDENTITY();
GO

Use a stored procedure to get a list of nvarchar(20) values as a parameter

I'm developing a SQL Server 2012 stored procedure.
I need to return a list of nvarchar(20) values, and I don't know how can I do it. I have tried this:
Using a Table-Valued Parameters but it must be READONLY.
Doing a select to return that list. But stored procedures only
returns INT values.
Any idea?
UPDATE:
This is what I have done:
CREATE PROCEDURE [dbo].[GetAggregationChildren]
#parentCode nvarchar(20),
#codeLevel tinyint output,
#returnValue int output
AS
declare #childsLevelCount tinyint
, #invalidChildCodesCount int;
set nocount on;
-- ======== VALIDATION ==========
if NULLIF(#parentCode, '') IS NULL
begin
set #returnValue = -19;
return NULL; -- Parameter null or empty.
end
-- Check if parent exists in AGGREGATIONS table.
if not exists (Select top 1 CODE from AGGREGATIONS where CODE = #parentCode)
begin
set #returnValue = -11;
return NULL;
end
set #childsLevelCount = (select count(c.CODE_LEVEL) from CODES c where c.CODE in (Select CODE from AGGREGATION_CHILDS where PARENT_CODE = #parentCode) group by c.CODE_LEVEL);
-- If this aggregation has children codes with different values, it is an error.
if (#childsLevelCount > 1)
begin
set #returnValue = -201;
return NULL;
end
-- =========== CODE =============
set #returnValue = 0;
set #codeLevel = (select c.CODE_LEVEL from CODES c where c.CODE in (Select CODE from AGGREGATION_CHILDS where PARENT_CODE = #parentCode) group by c.CODE_LEVEL);
select CODE from AGGREGATION_CHILDS where PARENT_CODE = #parentCode;
But, I have no idea about how to return the result of this select:
select CODE from AGGREGATION_CHILDS where PARENT_CODE = #parentCode;
This stored procedure returns this on SQL Server Management Studio:
It is also returning a 0. I thought that the stored procedure is going to return the select result only.
I need the result in a parameter because I going to use in a SQLCommand like this one:
SqlParameter childsParam = new SqlParameter();
childsParam.ParameterName = "#childs";
childsParam.SqlDbType = System.Data.SqlDbType.Structured;
childsParam.Direction = System.Data.ParameterDirection.Input;
childsParam.Value = tvp;
parameters = new List<SqlParameter>();
parameters.Add(childsParam);
SqlConnection connection =
_context.Database.Connection as SqlConnection;
connection.Open();
SqlCommand cmd = new SqlCommand();
cmd.CommandText = storedProcedureName;
cmd.CommandType = CommandType.StoredProcedure;
if (parameters != null)
cmd.Parameters.AddRange(parameters.ToArray());
cmd.Connection = connection;
cmd.ExecuteNonQuery();
connection.Close();
Stored procedure returns only Integer?
No this is not 100% true. If you are using RETURN to return the values from your stored procedure then your statement is true else it is false.
If you want to return string from your stored procedure then you can use SELECT like this:
CREATE PROCEDURE myProc
AS
BEGIN
SELECT 'test'
END
And to return multiple values you can use it like
CREATE PROCEDURE myProc
#Value1 varchar(20) OUTPUT,
#Value2 varchar(20) OUTPUT
AS
SELECT #Value1 = 'test1', #Value2 = 'test2'
and call it like
DECLARE #Value1 varchar(20), #Value2 varchar(20)
exec myProc #Value1 OUTPUT, #Value2 OUTPUT
SELECT #Value1, #Value1
Stored procedures return the type of the field in the SELECT statement. You can use CAST and CONVERT to change the types. For example:
SELECT CAST(field AS NVARCHAR(20))
With table value parameters you can set the field type on creation:
CREATE TYPE JobSpecifications AS TABLE
(JobName VARCHAR(50), AvailableDate Date );
you can use a temporary table to recuperate your list from the stored procedure, like the example below :
create proc Test
AS BEGIN
SELECT CAST('jkj' AS NVARCHAR(20)) value
END
DECLARE #tmp TABLE(value nvarchar(20))
INSERT INTO #tmp EXEC GhaziTest
SELECT * from #tmp

How can I make my stored procedure return a non zero if an insert inside of it fails?

I have this stored procedure:
CREATE PROCEDURE sp_purchase_test
#AdminTestId int,
#PurchaseDate DATETIME,
#UserId int
AS
INSERT INTO dbo.UserTest
(
AdminTestId,
PurchaseDate,
UserId,
Sequence
)
SELECT AdminTestId,
#PurchaseDate,
#UserId,
1
FROM AdminTest
WHERE AdminTestId = #AdminTestId
RETURN 0
I have a Unique index on UserTest.AdminTestId and sequence so there is a possibility that this stored
procedure will not work. How can I make the stored procedure return a non zero if the insert failed?
From what I understand it might be a good idea to use a TRY-CATCH. However if I do this should I do it like this:
BEGIN CATCH
SELECT -1
END CATCH
or
BEGIN CATCH
RETURN 99
END CATCH
I am confused between a SELECT at the end of a stored procedure and use or RETURN
Here's how I call the stored procedure. Ideally I would like to be able to return an error message from my C# method.
var sql = #"dbo.sp_purchase_test #AdminTestId,
#PurchaseDate,
#UserId";
SqlParameter[] parameters = new SqlParameter[]
{
new SqlParameter("#AdminTestId", adminTestId),
new SqlParameter("#PurchaseDate", DateTime.UtcNow),
new SqlParameter("#UserId", Int32.Parse(User.Identity.GetUserId()))
};
var result = db.Database.ExecuteSqlCommand(sql, parameters);
await db.SaveChangesAsync();
return Ok();
you can use output parameter
CREATE PROCEDURE test
#Value1 nvarchar(1),
#Value2 nvarchar(1),
#IsInserted bit output
AS
BEGIN
BEGIN TRY
INSERT INTO [dbo].[table_name]
([col_1]
,[col_2])
VALUES
(#Value1,#Value2)
SET #IsInserted=1
END TRY
BEGIN CATCH
SET #IsInserted=0
END CATCH
RETURN #IsInserted
END
Execute Procedure:
DECLARE #IsInserted bit
EXEC [dbo].[test]
#Value1 = N'a',
#Value2 = N'b',
#IsInserted = #IsInserted OUTPUT
SELECT #IsInserted as N'IsInserted'

stored procedure with out parameter

I have a stored procedure as follows:
ALTER PROCEDURE [dbo].[sp_CheckEmailAvailability] -- Add the
parameters for the stored procedure here ( #Email VARCHAR(50)=null,
#Count int OUTPUT
) AS BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements. SET NOCOUNT ON;
-- Insert statements for procedure here
SELECT #Count=COUNT(*) from dbo.tx_UserPersonalDetails where s_Email=#Email
END
I have the following code in my aspx.cs page:-
SqlCommand cmd = new SqlCommand("[dbo].[sp_CheckEmailAvailability]", objcon);
int result = 0;
try
{
cmd.CommandType = CommandType.StoredProcedure;
SqlParameter parm = new SqlParameter("#Email", SqlDbType.VarChar);
parm.Value = txtUserName.Text.ToString();
parm.Direction = ParameterDirection.Input;
cmd.Parameters.Add(parm);
SqlParameter parm1 = new SqlParameter("#Count", SqlDbType.Int);
// parm1.Value = txtUserName.Text.ToString();
parm.Direction = ParameterDirection.Output;
cmd.Parameters.Add(parm1);
cmd.Connection.Open();
result=cmd.ExecuteNonQuery();
if (result>0)
{
lblAvailText.Text = "Email id is in use";
}
else
{
lblAvailText.Text = "Email id is Available";
}
}
catch (SqlException sql)
{
}
finally
{
cmd.Connection.Close();
}
When I run the code , I am getting an error as :-
The formal parameter "#Email" was not declared as an OUTPUT parameter,
but the actual parameter passed in requested output.
Please help me with it.
Change
parm.Direction = ParameterDirection.Output;
to
parm1.Direction = ParameterDirection.Output;
You were setting the wrong SqlParam.
parm is used for the #Email param, which is initially correctly specified as Input, but then when you create parm1, you dont set its direction, you set parm's direction.
That is why you should use good naming conventions.
Alternative way - try to use RETURN -
ALTER PROCEDURE [dbo].[sp_CheckEmailAvailability]
(
#Email VARCHAR(50)
)
AS BEGIN
RETURN (
SELECT COUNT(1)
FROM dbo.tx_UserPersonalDetails
WHERE s_Email = #Email
)
END

Using Dapper.net to call stored procedure, always return -1 back

here's my stored procedure. when I test it, always get correct result back.
ALTER PROCEDURE [dbo].[AddSmoothieIngredients]
-- Add the parameters for the stored procedure here
#Query NVARCHAR(4000) ,
#SmoothieId INT ,
#CreatedDate DATETIME ,
#Status INT ,
#UserId INT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
BEGIN TRY
BEGIN TRAN
IF #SmoothieId > 0
BEGIN
DELETE FROM dbo.SmoothieIngredients
WHERE SmoothieId = #SmoothieId;
EXECUTE (#Query);
END
ELSE
BEGIN
IF #UserId = 0
SET #UserId = NULL;
INSERT INTO dbo.Smoothie
( Name, CreatedDate, Status, UserId )
VALUES ( N'', #CreatedDate, #Status, #UserId );
SET #SmoothieId = SCOPE_IDENTITY();
SET #Query = REPLACE(#Query, 'sId', #SmoothieId);
EXECUTE (#Query);
END
COMMIT TRAN
RETURN #SmoothieId
END TRY
BEGIN CATCH
ROLLBACK
END CATCH
END
However When I call this stored procedure using dapper.net, always return -1 back.
using (var conn = OpenConnection())
{
var parameter = new { Query = query, SmoothieId = smoothieId, CreatedDate = createdDate, Status = status, UserId = userId };
return conn.Execute("AddSmoothieIngredients", parameter, commandType: CommandType.StoredProcedure);
}
Probably, dapper.net cannot pick up the return value from the stored procedure. but I really dont know how to fix it. please help.
So the reason that Execute() returns -1 is because your sproc has SET NOCOUNT ON; which "suppresses the "xx rows affected" message after any DML" according to this question. Whether you want to disable that or not is another question also discussed in that link.
I just came across the same problem so I thought I'd throw in my 2 cents.
Found the solution, here's the sample code I found online. and it works.
var p = new DynamicParameters();
p.Add("#a", 11);
p.Add("#b", dbType: DbType.Int32, direction: ParameterDirection.Output);
p.Add("#c", dbType: DbType.Int32, direction: ParameterDirection.ReturnValue);
cnn.Execute("spMagicProc", p, commandType: commandType.StoredProcedure);
int b = p.Get<int>("#b");
int c = p.Get<int>("#c");
It looks like Dapper.net uses the SqlCommand.ExecuteNonQuery for the Execute method. This returns the number of rows affected, not the value of the return statement. What you're looking for is Query
return connection.Query<int>("AddSmoothieIngredients", parameter, commandType: CommandType.StoredProcedure).First();
Although I don't think that will capture the return statement either, in which case you'll need to alter the stored procedure to return a results set.
ALTER PROCEDURE [dbo].[AddSmoothieIngredients]
-- Add the parameters for the stored procedure here
#Query NVARCHAR(4000) ,
#SmoothieId INT ,
#CreatedDate DATETIME ,
#Status INT ,
#UserId INT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
BEGIN TRY
BEGIN TRAN
IF #SmoothieId > 0
BEGIN
DELETE FROM dbo.SmoothieIngredients
WHERE SmoothieId = #SmoothieId;
EXECUTE (#Query);
END
ELSE
BEGIN
IF #UserId = 0
SET #UserId = NULL;
INSERT INTO dbo.Smoothie
( Name, CreatedDate, Status, UserId )
VALUES ( N'', #CreatedDate, #Status, #UserId );
SET #SmoothieId = SCOPE_IDENTITY();
SET #Query = REPLACE(#Query, 'sId', #SmoothieId);
EXECUTE (#Query);
END
COMMIT TRAN
SELECT #SmoothieId
RETURN
END TRY
BEGIN CATCH
ROLLBACK
END CATCH
END
Or you could use another DB access method.
Recently I had to change an existing procedure to additionally return a value, and I had been using an anonymous type to pass the parameters. In this context, the really nice thing is that DynamicParameters supports an anonymous type as an input parameter, which makes this change easy to implement.
I had the following:
cnn.Execute("spMagicProc", new { a = 11, x = 13, y = 14, z = "something" },
commandType: commandType.StoredProcedure);
I was able change that to:
var p = new DynamicParameters(new { a = 11, x = 13, y = 14, z = "something" });
p.Add("#rval", dbType: DbType.Int32, direction: ParameterDirection.ReturnValue);
cnn.Execute("spMagicProc", p, commandType: commandType.StoredProcedure);
int rval = p.Get<int>("#rval");

Resources