I am unable to read value from stored procedure when executing it.
Here is my stored procedure
ALTER Procedure [dbo].[SaveInvitation]
(
#InvitationID INT OUTPUT,
#UserID INT,
#Email NCHAR(100),
#InvitationGUID UNIQUEIDENTIFIER OUTPUT
)
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRY
IF #UserID = -1
BEGIN
SET #UserID = NULL
END
IF NOT EXISTS (SELECT 1 FROM Invitations WHERE LTRIM(RTRIM(Email)) = LTRIM(RTRIM(#Email)))
BEGIN
INSERT INTO Invitations (UserID,
Email,
CreateDate)
VALUES (#UserID,
#Email,
GETDATE())
-- GET NEWLY INSERTED INVITATIONS ID
SET #InvitationID = IDENT_CURRENT('Invitations')
-- GET GUID FROM INVITATION ID
SELECT #InvitationGUID = InvitationGUID
FROM Invitations
WHERE InvitationID = #InvitationID
END
ELSE
BEGIN
RAISERROR('ALREADY_INVITATED', 16, 127)
END
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
BEGIN
ROLLBACK
END
EXEC ThrowError
END CATCH
END
I am executing this procedure from this function
My DataBaseModel.Designer.cs:
public ObjectResult<SaveInvitation_Result> SaveInvitation(ObjectParameter invitationID, Nullable<global::System.Int32> userID, global::System.String email, ObjectParameter invitationGUID)
{
ObjectParameter userIDParameter;
if (userID.HasValue)
{
userIDParameter = new ObjectParameter("UserID", userID);
}
else
{
userIDParameter = new ObjectParameter("UserID", typeof(global::System.Int32));
}
ObjectParameter emailParameter;
if (email != null)
{
emailParameter = new ObjectParameter("Email", email);
}
else
{
emailParameter = new ObjectParameter("Email", typeof(global::System.String));
}
return base.ExecuteFunction<SaveInvitation_Result>("SaveInvitation", invitationID, userIDParameter, emailParameter, invitationGUID);
}
It throws an exception
The data reader is incompatible with the specified 'TestModel.SaveInvitation_Result'. A member of the type, 'InvitationGUID', does not have a corresponding column in the data reader with the same name.
I have created a complex type i.e. SaveUserRegistration_Result and imported one function SaveInvitation of return type SaveUserRegistration_Result.
How can I solve above exception? Is there any change in stored procedure?
Screen shot
It's giving you this error because you're not actually SELECTing back a result. If you added a line to the end of your procedure for example:
SELECT #InvitationGUID AS InvitationGUID
It should work just fine for you.
Related
I'm a beginner at C#. I can't call a stored procedure.
My stored procedure is this:
CREATE PROCEDURE USP_login
#us VARCHAR(20),
#pwd VARCHAR(20)
AS
BEGIN TRAN
BEGIN TRY
SELECT *
FROM dbo.KhachHang
WHERE tenDangNhap = #us AND matKhau = #pwd
END TRY
BEGIN CATCH
ROLLBACK TRAN
RETURN 0
END CATCH
COMMIT TRAN
RETURN 1
GO
In my C# code, I use this function to call the USP_login stored procedure but it doesn't work:
public bool loginStored(string us, string pwd)
{
object[] sqlParams =
{
new SqlParameter ("#userName", us),
new SqlParameter ("#passWord", pwd),
};
var rs = db.Database.SqlQuery<bool>("USP_login #userName, #passWord", sqlParams).SingleOrDefault();
return rs;
}
Error message in screenshot:
Looks like SELECT * ... is returning more than just a single bool. (Based on the query, clearly the table has at least two fields, tenDangNhap and matKhau.) But that's what you told the code to expect:
db.Database.SqlQuery<bool>(/.../)
Either select only the column you want:
SELECT SomeBooleanValue FROM dbo.KhachHang WHERE tenDangNhap=#us AND matKhau=#pwd
Or specify the correct type that can be expected for each record (which may be a custom class that you need to define):
db.Database.SqlQuery<SomeObjectType>(/.../)
I have a SQL Stored Procedure that works like this: it updates the Confirmed column in a PaymentRecords table, which has a default value of 0, to be either 1 (approved) or 2 (rejected). To ensure concurrency control, it prevents updates from occurring if the Confirmed value is already 1 or 2.
When the update is successful, it returns a string 'SUCCESS', where as if it fails, it returns the chequeNumber to display a message saying that particular cheque has already been updated. Both of these are returned to a C# web service.
SQL:
ALTER PROCEDURE [dbo].[UpdateUnverifiedChequeConfirmed]
-- Add the parameters for the stored procedure here
#id uniqueidentifier,
#confirmed int,
#rejectReason nvarchar(MAX),
#bankName nvarchar(MAX)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE #existingConfirmed int;
-- Insert statements for procedure here
SELECT TOP 1 #existingConfirmed = [PaymentRecords].[confirmed]
FROM [dbo].[PaymentRecords]
INNER JOIN [dbo].[ChequePayments]
ON [ChequePayments].[paymentRecordID] = [PaymentRecords].[id]
WHERE [ChequePayments].[id] = #id
IF #existingConfirmed = '1' OR #existingConfirmed = '2'
BEGIN
PRINT 'Cheque already updated by another user, further changes not allowed.';
--SELECT 0 AS FAIL
SELECT [ChequePayments].[chequeNo] AS chequeNo
FROM [ChequePayments]
WHERE [ChequePayments].[id] = #id
END
ELSE
BEGIN
UPDATE [dbo].[PaymentRecords]
SET confirmed = #confirmed, rejectReason = #rejectReason, [timestamp] = GETDATE()
FROM [dbo].[PaymentRecords]
INNER JOIN [dbo].[ChequePayments]
ON [ChequePayments].[paymentRecordID] = [PaymentRecords].[id]
WHERE [ChequePayments].[id] = #id
UPDATE [dbo].[ChequePayments]
SET bankName = #bankName, [timestamp] = GETDATE()
WHERE [ChequePayments].[id] = #id
--SELECT 1 AS SUCCESS
SELECT 'SUCCESS' AS SUCCESS
END
END
C#:
unverifiedChequeNumberResponse ICommon.updateUnverifiedCheque(unverifiedChequeRequest request, string id)
{
string result, rejectReason;
Guid newID;
if (request.bankName == "Select a Bank")
{
return new unverifiedChequeNumberResponse("Error: No bank selected.");
}
if (request.rejectReason == "")
{
rejectReason = null;
}
else
{
rejectReason = request.rejectReason;
}
if (Guid.TryParse(id, out newID))
{
try
{
result = dbkl.UpdateUnverifiedChequeConfirmed(newID, request.confirmed, rejectReason, request.bankName).FirstOrDefault();
}
catch (Exception ex)
{
if (isDebug() == true)
{
return new unverifiedChequeNumberResponse(ex.Message);
}
else
{
return new unverifiedChequeNumberResponse("Error: Database inaccessible");
}
}
if (result == "SUCCESS")
{
return new unverifiedChequeNumberResponse();
}
else
{
unverifiedChequeNumberResponse response = new unverifiedChequeNumberResponse();
response.chequeNo = result;
return new unverifiedChequeNumberResponse("Error: Cheque " + response.chequeNo + " already updated by another user.");
}
}
else
{
return new unverifiedChequeNumberResponse("Error: Invalid ID.");
}
}
Although all of this is working properly, I need to make a change so that the SP returns a 1 when successful and 0 when unsuccessful. The two issues I am facing are:
How to make the unsuccessful update path in the SP return 2 values, namely 0 and the chequeNumber? If I write it as:
SELECT 0 AS FAIL, [ChequePayments].[chequeNo] AS chequeNo
FROM [ChequePayments]
WHERE [ChequePayments].[id] = #id
it ends up returning a null value and throws an exception.
If the chequeNumber has a value of 1 (unlikely in practice but still worth covering) and the update is denied, the web service will still return the success message. Is there a way to change it so that the service can differentiate between 1 as a chequeNumber and 1 as a Success value?
You could look at using SQL Output parameters. For example;
ALTER PROCEDURE [dbo].[UpdateUnverifiedChequeConfirmed]
-- Add the parameters for the stored procedure here
#id uniqueidentifier,
#confirmed int,
#rejectReason nvarchar(MAX),
#bankName nvarchar(MAX),
#result1 int output,
#result2 varchar(50) output
AS
BEGIN
-- do stuff...
set #result1 = 123
set #result2 = 'success'
END
You would have to update your .Net code to define the output paramaters then read their values after the call.
You might also want to consider using transactions for concurrency control - that's what they're for, and they will provide a more robust solution than you have here.
I want to read Scope_Identity via output variable '#RoleID' from where I am assigning value of scope identity.
C#:
private static long createNewRoleInsert(ADB.Model.RolesModel roleModelObj, MSSQL sql)
{
bool killConnection = Utils.getConnection(ref sql);
long returnValue = 0;
try
{
sql.SetSProc("[dbo].[p_Role_dfn_createNew]");
sql.AddParam("#Title", roleModelObj.Title);
sql.AddParam("#Description", roleModelObj.Description);
sql.AddParam("#CreatedDate", roleModelObj.CreatedDate);
var RoleID = sql.ExecuteNonQuery();
if(RoleID!=0 && RoleID>0)
{
returnValue = RoleID;
}
}
finally
{
if (killConnection)
sql.Dispose();
}
return returnValue;
}
Stored procedure:
SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[p_Role_dfn_createNew]
#Title nvarchar(250),
#Description nvarchar(MAX) = NULL,
#CreatedDate DateTime,
#RoleID bigInt OUTPUT
AS
SET NOCOUNT ON;
SET XACT_ABORT ON
DECLARE #l_object AS SYSNAME = OBJECT_NAME(##PROCID),
#l_error_msg AS NVARCHAR(2000)
BEGIN TRY
BEGIN TRAN
INSERT INTO [adb_TestDb].[dbo].[Role] ([Title], [Description], [CreatedDate])
VALUES (#Title, #Description, #CreatedDate)
COMMIT TRAN
SET #RoleID = SCOPE_IDENTITY();
RETURN #RoleID
END TRY
BEGIN CATCH
-- rollback any open/uncomitted transactions
IF XACT_STATE() IN ( -1, 1) ROLLBACK TRANSACTION
-- return an error containing the object, error number and error description
SELECT #l_error_msg = 'Error number : ' + CAST(ERROR_NUMBER()AS VARCHAR) + '. ' + ERROR_MESSAGE()
RAISERROR (#l_error_msg,16,1)
END CATCH
The ExecuteNonQuery method doesn't return the return value from the procedure, it returns the number of rows affected.
To get the return value you would add a parameter with ParameterDirection.ReturnValue, however that won't safely get you the value in #RoleID as the return value from a procedure can't be a bigint, it's always an int.
As you already have #RoleID as an output parameter you should add parameter to the command to get the value. Example:
SqlParameter roleIdParam = new SqlParameter("#RoleID", SqlDbType.BigInt);
roleIdParam.Direction = ParameterDirection.Output;
cmd.Parameters.Add(roleIdParam);
// execute command
long roleId = (long)roleIdParam.Value;
You need to add an output parameter in C# to get the value of #RoleID from the stored procedure. Here's an example of that:
using System.Data.SqlClient;
using (SqlConnection conn = new SqlConnection("connectionString"))
{
SqlCommand cmd = conn.CreateCommand();
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.CommandText = "dbo.p_Role_dfn_createNew";
// add other parameters...
cmd.Parameters.Add(new SqlParameter("#RoleID", SqlDbType.BigInt))
.Direction = ParameterDirection.Output;
cmd.ExecuteNonQuery();
returnValue = (long)cmd.Parameters["#RoleID"].Value;
}
Change the
RETURN #RoleID
to
SELECT #RoleID
or add the output parameter as explained in other answers
I have a stored procedure that runs a insert statement and it returns a value into a datatable. I must use a datatable thank you in advance :)
my SQL stored procedure
begin
Declare #postID int = 0
insert into reviews (customerID, title, review)
values (#cusomerID, #title, #review)
set #postID = SCOPE_IDENTITY()
end
the return value is stored in a datatable i would like to check if the insert was successful.
i'm using ajax jquery to do insert which calls the stored procedure and returns a value
method called
if (data.Rows[0]["post_ID "] != 0)
{
return "successful";
}
else{
return "failed";
}
in javascript
if(result != "failed"){
run code
}
however it is returning successful even when the insert failed.
any help would be great thank you.
The SCOPE_IDENTITY() returns the last auto-id. In your case it's returning the one before your failed insert.
Try something like
BEGIN
DECARE #postID int = 0
INSERT INTO reviews (customerID, title, review) VALUES (#cusomerID, #title, #review)
IF ##ERROR <> 0
SET #postID = -1
ELSE
SET #postID = (select SCOPE_IDENTITY())
SELECT #postID
END
and in your code, if ID == -1 you know it failed.
Alternatively, you could
BEGIN
SET XACT_ABORT ON
DECARE #postID int = 0
INSERT INTO reviews (customerID, title, review) VALUES (#cusomerID, #title, #review)
SET #postID = (select SCOPE_IDENTITY())
SELECT #postID
END
and an exception will occur in your code upon any error (transaction is rolled back too).
I have a stored procedure that is called to validate a user during login.
If success it returns the user entity, and that works good! My question is if it doesn't work, I'll raise an error in the SP, How do I catch this error and use it in the best way? Right now I'm getting nullrefference, this is the code:
Store procedure:
ALTER PROCEDURE getEmployee
(
#username nvarchar(50),
#password nvarchar(50)
)
AS
DECLARE #Error_MSG nvarchar(50)
BEGIN
IF EXISTS (select * from Employee where eUsername = #username AND pword = #password)
begin
select * from Employee where eUsername = #username AND pword = #password
END
ELSE
BEGIN
SET #Error_MSG = 'Wrong password, or user doesnt exist'
RAISERROR (#Error_MSG, 11,1)
END
END
And in the code it looks like this, the SP is getEmployee
ActivityDatabaseDataContext dc = new ActivityDatabaseDataContext();
Employee emp;
public bool logIn(string piUsername, string piPassword)
{
try
{
emp = dc.getEmployee(piUsername, piPassword).Single();
}
catch (Exception ex)
{
errorMsg = ex.Message + ex.InnerException.Message;
}
if (emp != null)
{
AppHelper.AppHelper.setUser(emp);
return true;
}
else
{
return false;
}
My question is how I should handle the exception?
I wouldn't generally raise an error from a SP unless it was actually a system problem with the operation. Entering the wrong username and password is a user problem, and one you need only deal with at the interface level, so I'd throw most of that SP away and deal with the two use cases (1 row or 0 rows returned) in the business layer or interface code. If 0 rows, throw up the "Wrong username or password" message to the client and if 1, log in.
ALTER PROCEDURE getEmployee
(
#username nvarchar(50),
#password nvarchar(50)
)
AS
BEGIN
select * from Employee where eUsername = #username AND pword = #password
END
Your InnerException is probably null.
You should try to catch and deal with specific exceptions, in this case SqlExceptions.
ALTER PROCEDURE getEmployee
(
#username nvarchar(50),
#password nvarchar(50)
)
AS
BEGIN
select * from Employee where eUsername = #username AND pword = #password
END
...
SqlCommand cmd = new SqlCommand("getEmployee", conn);
cmd.AddWithValue('#username', name);
cmd.AddWithValue('#password', pass);
SqlAdapter da = new SqlAdapter(cmd);
DataSet ds= new DataSet();
da.Fill(ds);
if (ds.Table.Count > 0 && ds.Table.Rows.Count == 1) {
// success
} else {
// fail
}
IF(#Count>0)
BEGIN
SELECT #RetVal = 6
, #ErrMsg = 'A description with the same name exists. Please provide a unique name.'
GOTO ERROR
END
Use the inbuilt StoredProcException in catch, that implies:
catch (StoredProcException spEx)
{
switch (spEx.ReturnValue)
{
case 6:
UserMessageException umEx= new UserMessageException(spEx.Message);
throw umEx;
}
}
You can pass the Message as string instead of spEx.Message