Dapper output parameter is not returning values - dapper

This is my code part:
CResponseVO objCResponseVO = new CResponseVO();
try
{
var parameters = new DynamicParameters();
parameters.Add("#UserId", currentUser.userId, DbType.Int32);
parameters.Add("#Operation", operation, DbType.String);
parameters.Add("#Output", dbType: DbType.Int32, direction: ParameterDirection.Output);
using (var connection = SqlAccessHelper.SqlHelper.GetOpenConnection(SqlConnectionHelper.SqlConnectionString()))
{
var reader = connection.QueryMultiple("USP_DataExtract", parameters, (SqlTransaction)null, 1000000, CommandType.StoredProcedure);
int result = parameters.Get<int>("#Output");
if (operation != "insert")
{
ObservableCollection<DataExtraction.DataExtractionTracker> DataExtractionTracker = new ObservableCollection<DataExtraction.DataExtractionTracker>(reader.Read<DataExtraction.DataExtractionTracker>());
objCResponseVO.addObject("ExtractionStatus", DataExtractionTracker);
}
objResponseVO.Result = result;
}
This is my SP, and I have used try and catch to commit or rollback based on ouput parameter value:
#UserID int=0,
#Operation varchar(50)= NULL,
#Output INT OUTPUT
AS
BEGIN
BEGIN TRY
BEGIN TRANSACTION
If(#Operation = 'select')
BEGIN
SELECT RequestId, UserId, RequestTime, Status,DownloadPath from DataExtractTracker where UserId= #UserID
END
If(#Operation = 'insert')
BEGIN
Insert into DataExtractTracker( UserId, RequestTime, Status) values (#UserID, GETDATE(), 'Waiting')
END
SET #Output = 0
COMMIT TRANSACTION
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
SET #Output = 1
DECLARE #ErrorMessage NVARCHAR(4000);
DECLARE #ErrorSeverity INT;
DECLARE #ErrorState INT;
SELECT #ErrorMessage = ERROR_MESSAGE(), #ErrorSeverity = ERROR_SEVERITY(), #ErrorState = ERROR_STATE();
-- Use RAISERROR inside the CATCH block to return
-- error information about the original error that
-- caused execution to jump to the CATCH block.
RAISERROR (#ErrorMessage, -- Message text.
#ErrorSeverity, -- Severity.
#ErrorState -- State.);
END CATCH
END
However, I am not able to retrieve the ouput parameter value. I am getting an exception when executing:
int result = parameters.Get<int>("#Output");
Exception says something like this:
Object reference not set to an instance of an object.

This is simply a feature of TDS, and you would get the same with ADO.NET; the returned parameter values are usually at the end of the TDS stream; consequently, the updated values are not available until after you have finished consuming the data.
Basically, you need to query the parameter values after you have finished with reader, because until then the value has not come back. For example, the following hastily added integration test passes:
public void TestOutputParameter()
{
connection.Execute(#"
create proc #TestOutputParameterProc #Foo int, #Bar int out as
set #Bar = #Foo select 1 as [A] select 2 as [B]");
try
{
var args = new DynamicParameters(new { Foo = 123 });
args.Add("#Bar", dbType: DbType.Int32,
direction: ParameterDirection.Output);
using (var grids = connection.QueryMultiple("#TestOutputParameterProc",
args, commandType: CommandType.StoredProcedure))
{
// this will fail here; we have not consumed the TDS data yet!
// args.Get<int>("#Bar").IsEqualTo(123);
// note we don't *have* to read the data here; disposing "grids"
// would be enough to skip to the end of the TDS
grids.Read<int>().Single().IsEqualTo(1); // A
grids.Read<int>().Single().IsEqualTo(2); // B
}
// at this point we have consumed the TDS data, so the parameter
// values have come back to the caller
args.Get<int>("#Bar").IsEqualTo(123);
}
finally
{ // clean up the proc
connection.Execute("drop proc #TestOutputParameterProc");
}
}

Related

Stored procedure returns wrong value in asp.net web api

I wrote some procedure code which is return value 1 when it runs successfully.
But it always returns -1(Failed value) when it goes well in ASP.NET Web API.
I tested in SSMS with this case.
USE [MY_DATABASE]
GO
DECLARE #return_value int
EXEC #return_value = [dbo].[API_TO_WEB_CREATE_RESOURCE]
#RES_Size = 3019,
#RES_ContentType = N'image/jpeg',
#RES_OriginalName = N'evolving_google_identity_share.jpg',
#RES_GUID = N'b98bd7ee-cb19-49c8-a8dc-3b92b3210b91',
#RES_Path = N'~/Content/resources\\b98bd7ee-cb19-49c8-a8dc-3b92b3210b91',
#RES_Upload_USR_Index = NULL
SELECT 'Return Value' = #return_value
GO
This returns 1
Same code in ASP.NET Web API.
int result = context.API_TO_WEB_CREATE_RESOURCE(
3019,
"image/jpeg",
"evolving_google_identity_share.jpg",
"b98bd7ee-cb19-49c8-a8dc-3b92b3210b91",
"~/Content/resources\\b98bd7ee-cb19-49c8-a8dc-3b92b3210b91",
null
);
This returns -1
And this is my procedure.
USE [MY_DATABASE]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author:
-- Create date:
-- Description:
-- =============================================
ALTER PROCEDURE [dbo].[API_TO_WEB_CREATE_RESOURCE]
-- Add the parameters for the stored procedure here
#RES_Size int = 0,
#RES_ContentType nvarchar(100) = NULL,
#RES_OriginalName nvarchar(300),
#RES_GUID nvarchar(50),
#RES_Path nvarchar(500),
#RES_Upload_USR_Index int = NULL
AS
BEGIN
DECLARE #RES_RegisteredDatetime datetime = GETDATE()
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
BEGIN TRY
BEGIN TRAN
INSERT INTO
dbo.NS_Resource (RES_Size, RES_ContentType, RES_OriginalName, RES_GUID, RES_Path, RES_Upload_USR_Index)
VALUES (#RES_Size, #RES_ContentType, #RES_OriginalName, #RES_GUID, #RES_Path, #RES_Upload_USR_Index);
PRINT '리소스 추가됨'
COMMIT TRAN
RETURN(1);
END TRY
BEGIN CATCH
ROLLBACK TRAN
DECLARE #ERROR_Msg nvarchar = error_message()
RAISERROR('리소스 추가 중 문제 발생됨 %s', 16, 1, #ERROR_Msg)
RETURN (-1)
END CATCH
END
Summary
Expected
Return value 1 and insert new value successfully.
Actual
In SSMS
Return Value 1 and insert new value successfully.
In ASP.NET Web API (This is the problem)
Return Value -1 and insert new value successfully.
It's never a good idea to have multiple return statements in a code module, and SQL is not an exception. Try to rewrite your procedure as follows and see if it will help:
ALTER PROCEDURE [dbo].[API_TO_WEB_CREATE_RESOURCE]
-- Add the parameters for the stored procedure here
#RES_Size int = 0,
#RES_ContentType nvarchar(100) = NULL,
#RES_OriginalName nvarchar(300),
#RES_GUID nvarchar(50),
#RES_Path nvarchar(500),
#RES_Upload_USR_Index int = NULL
AS
DECLARE #RES_RegisteredDatetime datetime = GETDATE();
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
declare #Ret int = -1; -- Failure by default
BEGIN TRY
BEGIN TRAN
INSERT INTO
dbo.NS_Resource (RES_Size, RES_ContentType, RES_OriginalName, RES_GUID, RES_Path, RES_Upload_USR_Index)
VALUES (#RES_Size, #RES_ContentType, #RES_OriginalName, #RES_GUID, #RES_Path, #RES_Upload_USR_Index);
PRINT '리소스 추가됨';
COMMIT TRAN
set #Ret = 1; -- Success
END TRY
BEGIN CATCH
if ##trancount > 0
ROLLBACK TRAN;
DECLARE #ERROR_Msg nvarchar(2048) = error_message();
RAISERROR('리소스 추가 중 문제 발생됨 %s', 16, 1, #ERROR_Msg);
END CATCH;
return #Ret;
go
However, the whole thing looks a bit cumbersome to me. If you are throwing the error to the client from inside the catch block, this should be enough. It will translate into an SQLException in C#, so return value becomes rather irrelevant.
Personally, I don't use the return value; instead, I usually create 2 output parameters, int for the number and nvarchar(2048) for the error message, and assign their values in the catch block without re-throwing. When execution is completed I simply check the value of the #ErrorNumber output parameter, and if it's neither zero nor NULL, I process the error on the client side.

Mapping to complex type (Entity Framework) giving error

I am using Entity Framework database first approach. I am creating complex types for my stored procedure so that I can use it in my ASP.NET MVC application. I need to create the complex type as Entity Framework doesn't create it for me. I am unable to map the complex type with entity class that created. I am getting an error
Cannot implicitly convert type 'System.Collections.Generic.List' to 'int'
Could somebody tell me what the problem is?
Stored procedure:
CREATE PROCEDURE [dbo].[spUserExists]
(#NetworkID varchar(20),
#Domain varchar(50),
#RoleID int)
AS
BEGIN
set nocount on
----------------------------------------
-- variables
----------------------------------------
-- error
declare #ErrorMessage nvarchar(2048), #ErrorSeverity int, #ErrorState int
----------------------------------------
-- exists
----------------------------------------
declare #UserExists int; set #UserExists = 0
begin try
select #UserExists = 1
where exists (Select up.NetworkID, up.Domain, ur.RoleId
from dbo.UserProfile up
inner join UserProfileRoleLink ur on up.UserProfileId = ur.UserProfileId
where up.NetworkID = #NetworkID
and up.Domain = #Domain
and ur.RoleId = #RoleID)
end try
begin catch
select
#ErrorMessage = dbo.GetErrorMessage('Exists', error_message(), object_schema_name(##procid), object_name(##procid), error_line()),
#ErrorSeverity = error_severity(),
#ErrorState = error_state();
raiserror(#ErrorMessage, #ErrorSeverity, #ErrorState);
return
end catch
select #UserExists as UserExists
END
Entity class:
public class ValidUser
{
public Int32 UserExists { get; set; }
}
Function import:
Complex type:
Mapping and returning value:
public static System.Int32 UserValidate()
{
using (var db = new MCREntities())
{
var isValid = db.spUserExists(GetShortname(), GetDomain(), 1);
return Mapper.Map<List<ValidUser>>(isValid);
}
}
I think you need the method UserValidate return type to be the List instead of Int32.

Error Handle in sql server

DECLARE #id bigint=0,
#id int=0,
#name varchar(50) = '36',
#marks int = 'SDFGS'
#Op varchar(50) = 'UPSERT'
IF(#Op='UPSERT')
BEGIN
INSERT INTO tbl_student
(name, marks)
VALUES
(#name, #marks)
SELECT SCOPE_IDENTITY()
END
ELSE
BEGIN
UPDATE tbl_student SET
name = #name,
marks = #marks
WHERE id = #id
SELECT 'Success'
END
It throw error 'Conversion failed when converting the varchar value 'SDFGS' to data type int.'
I want to handle this error.
If error then it will be return 'Error' string.
You can handle this error using TRY... CATCH Block
Begin
declare #msg varchar(100)
Begin try
DECLARE #id bigint=0,#name varchar(50) = '36',#marks int = 'SDFGS',#Op varchar(50) = 'UPSERT'
IF(#Op='UPSERT')
BEGIN
INSERT INTO tbl_student
(name, marks)
VALUES
(#name, #marks)
SELECT SCOPE_IDENTITY()
END
ELSE
BEGIN
UPDATE tbl_student SET
name = #name,
marks = #marks
WHERE id = #id
SELECT 'Success'
Set #msg='Success'
END
End try
Begin catch
SELECT 'Error'
Set #msg='Error'
End catch
End
You can use TRY ... CATCH
https://msdn.microsoft.com/en-us/library/ms175976.aspx - there is a sample code here.
The error says it all, you are trying to put a string value in an int datatype and hence the error. If you want to catch this error then try to use TRY...CATCH. Something like
BEGIN TRY
-- Your code.
END TRY
BEGIN CATCH
-- Catch the exception/error here.
END CATCH;

Get RETURN value from Proc

I have a SQL Server procedure which returns a result set, and is working well. I am adding error handling (TRY/CATCH), and in the event of an error, want to RETURN -1 to indicate an issue (Or another negative integer to maybe know the reason for the failure).
Is there a way to get the return value (As well as the result set, if required) from the procedure call?
I call the procedure like this:
Context.project_scheduled_event_payments(st.id);
In this case, I don't get a result set, as I don't need one. But I would like to know if the proc returned with NULL (All went fine), or a negative number indicating an issue:
IF(#EndDate IS NOT NULL)
BEGIN
BEGIN TRY
UPDATE scheduled_event_transaction
SET Deleted = GETUTCDATE(),
lastupdateuser = 0,
lastupdatedate = GETUTCDATE()
WHERE Scheduled_Event_ID = #scheduled_event_id
AND scheduled_payment_date > #EndDate
END TRY
BEGIN CATCH
ROLLBACK
RETURN -1
END CATCH
END
RETURN -- Success
As suggested by SystemOnline, refer get-return-value-from-stored-procedure-in-asp-net.
But I will suggest to throw error & catch in C#/vb code like:
BEGIN TRY
...
END TRY
BEGIN CATCH
DECLARE #ErrorMessage NVARCHAR(4000);
DECLARE #ErrorSeverity INT;
DECLARE #ErrorState INT;
SELECT
#ErrorMessage = ERROR_MESSAGE(),
--#ErrorSeverity = ERROR_SEVERITY(),
#ErrorState = ERROR_STATE();
RAISERROR (#ErrorMessage, -- Message text.
16, --#ErrorSeverity,-- Severity.
#ErrorState -- State.
);
END CATCH;
Please note that I have explicitly set severity to 16 so that error is thrown to code.

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