Raise user defined SQL error and capture it in code - c#

I'm trying to raise a user defined error from one stored procedure to a c# code. I have a stored procedure that assign employees to users, if you already assigned an employee to user 1 for example and you try to do the same thing again, the stored procedure should raise an error saying, "This association already exists" and return a code, for example 1 and a description of the error. The problem is that in c sharp is not passing by the catch part. here is the code:
public bool InsertUser(Guid userId, string[][] IDs, AppParams myParams)
{
bool flag = false;
SqlTransaction transaction = null;
using (var dbConn = new SqlConnection(this.ConnectionString))
{
using (var cmd = new SqlCommand())
{
try
{
dbConn.Open();
cmd.Connection = dbConn;
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "InsertUser";
transaction = dbConn.BeginTransaction();
cmd.Transaction = transaction;
for (int i = 0; i < IDs.Length; i++)
{
flag = false;
cmd.Parameters.Clear();
cmd.Parameters.AddWithValue("#UserId", userId);
cmd.Parameters.AddWithValue("#EmployeeId", IDs[i][0]);
cmd.Parameters.AddWithValue("#CompanyId", myParams.ApplicationId);
cmd.Parameters.AddWithValue("#ModifiedUserId", myParams.User.UserId);
//add output parameter
cmd.Parameters.Add("#ID", SqlDbType.Int, 4);
cmd.Parameters["#ID"].Direction = ParameterDirection.Output;
cmd.Parameters.Add("#ReturnValue", SqlDbType.Int, 4);
cmd.Parameters["#ReturnValue"].Direction = ParameterDirection.ReturnValue;
cmd.Parameters.Add("#ErrorMsg", SqlDbType.VarChar, 300);
cmd.Parameters["#ErrorMsg"].Direction = ParameterDirection.Output;
cmd.ExecuteNonQuery();
var returnValue = Convert.ToInt32(cmd.Parameters["#ReturnValue"].Value);
flag = returnValue.Equals(0);
if (!flag)
{
if (cmd.Parameters["#ErrorMsg"].Value != DBNull.Value)
{
this.ErrorModel.HasError = true;
this.ErrorModel.ErrorMessage = cmd.Parameters["#ErrorMsg"].Value.ToString();
}
transaction.Rollback();
return false;
}
}
transaction.Commit();
}
catch (SqlException sqlex)
{
this.ErrorModel.HasError = true;
this.ErrorModel.ErrorMessage = sqlex.Message;
}
catch (Exception ex)
{
this.ErrorModel.HasError = true;
this.ErrorModel.ErrorMessage = ex.Message;
}
}
}
return flag;
}
Am I doing something wrong?
here is my SQL code:
ALTER PROCEDURE [dbo].[InsertUser]
(
#UserId uniqueidentifier,
#EmployeeId int,
#CompanyId uniqueidentifier,
#SystemStatusId int = 1,
#ModifiedByUserId uniqueidentifier,
#ID int output,
#ErrorMsg nvarchar(300) = NULL output
)
AS
BEGIN
DECLARE #ReturnVal int
SET NOCOUNT ON;
SET #ReturnVal = 0;
SET #ErrorMsg = null;
-- check for existing combination
IF EXISTS(SELECT 1 FROM [dbo].[UserRel]
WHERE [UserId] = #UserId
AND [EmployeeId] = #EmployeeId
AND [CompanyId] = #CompanyId
AND [SystemStatusId] = 1)
BEGIN
SET #ReturnVal = 1;
SET #ErrorMsg = 'Item already Exist in Database'
RAISERROR #ErrorMsg, 0, 0
GOTO ProcedureExit;
END
-- Insert statement
INSERT [dbo].[UserRel]
(
[UserId],
[EmployeeId],
[CompanyId],
[SystemStatusId],
[ModifiedByUserId],
[ModifiedDate]
)
VALUES
(
#UserId,
#EmployeeId,
#CompanyId,
#SystemStatusId,
#ModifiedByUserId,
sysdatetimeoffset()
)
IF( ##ERROR <> 0 )
BEGIN
SET #ReturnVal = 2;
SET #ErrorMsg = 'Failed to INSERT [dbo].[InsertUser]'
GOTO ProcedureExit;
END
ELSE
SET #ID = SCOPE_IDENTITY();
ProcedureExit:
RETURN #ReturnVal;
END

The problem is that in c sharp is not passing by the catch part.
Because in the C#, there was no exception. You simply checked the return value of the stored procedure, and then decided to rollback and return false;. If you want an exception: throw an exception.
Alternatively, if the SQL issues a raiserror with severity 16 or higher, an exception will be observed automatically.
Incidentally, it is not clear that your transaction is correctly terminated in the case of a genuine exception. I would suggest moving the rollback code into the catch block. Since you do the exact same thing regardless of which exception you catch, there is no benefit in the catch (SqlException sqlex) block - so you might as well remove that.

Related

Procedure or function 'sp_Lab_InsertBiologicalPersonnel' expects parameter '#IcNumber', which was not supplied

I am trying to insert data into a SQL Server database by calling a stored procedure, but I am getting the error
Procedure or function 'sp_Lab_InsertBiologicalPersonnel' expects parameter '#IcNumber', which was not supplied
My stored procedure is called sp_Lab_InsertBiologicalPersonnel. I already check many time if there something missing, but still get same error, even make comparison with old code which is working. But for my code it say that error.
This is my C# code
public static PostApiResponse InsertBiologicalPersonel(Adapter ad, BiologicalPersonelInsert request, int creatorUserId)
{
var response = new PostApiResponse();
try
{
using (SqlCommand command = new SqlCommand("sp_Lab_InsertBiologicalPersonnel", ad.SQLConn, ad.SQLTran))
{
SqlParameter Param = new SqlParameter();
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add(new SqlParameter() { Value = request.biologicalId, ParameterName = "#biologicalId" });
command.Parameters.Add(new SqlParameter() { Value = request.IcNumber, ParameterName = "#IcNumber" });
command.Parameters.Add(new SqlParameter() { Value = request.Position, ParameterName = "#Position" });
command.Parameters.Add(new SqlParameter() { Value = creatorUserId, ParameterName = "#IdUserCreator" });
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
response.ResponseMessage = reader.GetString(0);
response.ReturnCode = reader.GetInt32(1);
response.Id = Convert.ToInt32(reader.GetValue(2));
}
reader.Close();
}
}
}
catch (Exception e)
{
throw e;
//string context = Common.ToStr(System.Web.HttpContext.Current);
//clsErrorLog.ErrorLog(context, e);
}
return response;
}
And this is my stored procedure:
ALTER PROCEDURE [dbo].[sp_Lab_InsertBiologicalPersonnel]
#biologicalId INT,
#IcNumber INT ,
#Position NVARCHAR(50),
#IdUserCreator INT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #ReturnCode Int
DECLARE #ReturnMessage NVARCHAR(200)
DECLARE #Identity INT
INSERT INTO [dbo].[Lab_BiologicalPersonnel]
([Id]
--,[Name]
,[IcNumber]
,[Position]
,[IdUserCreator]
,[IsDeleted]
,[IsEnabled])
VALUES
(#biologicalId
--,#Name
,#IcNumber
,#Position
,#IdUserCreator
,0
,1)
IF ##ROWCOUNT = 0
BEGIN
SET #ReturnCode = 1
SET #ReturnMessage = 'Insert Fail'
SET #Identity = 0
END
ELSE
BEGIN
SET #ReturnCode = 0
SET #ReturnMessage = 'Insert Success'
SET #Identity = (SELECT SCOPE_IDENTITY())
END
SELECT #ReturnMessage, #ReturnCode, #Identity
END
Please help me explain in quite simple, I'm a newbie in programming.

How can I select a field from Microsoft SQL Server to send to c#?

I have a table with SystemId as the primary key. I want to select #systemid and send it to the c# form.
Below is c# code
SqlCommand cmdfindsystemid = new SqlCommand("FindSystemId", con);
cmdfindsystemid.CommandType = CommandType.StoredProcedure;
cmdfindsystemid.Parameters.Add("#systemid", SqlDbType.Int);
cmdfindsystemid.Parameters["#systemid"].Direction = ParameterDirection.Output;
int systemid = Convert.ToInt32(cmdfindsystemid.Parameters["#systemid"].Value);
But I don't know the SQL code. It can be something like:
create proc FindSystemId
#systemid int output
as
select SystemId from tbl systems
set #systemid ?????
You must call the ExecuteNonQuery method from the SqlCommand before reading the parameter. For sample:
SqlCommand cmdfindsystemid = new SqlCommand("FindSystemId", con);
cmdfindsystemid.CommandType = CommandType.StoredProcedure;
cmdfindsystemid.Parameters.Add("#systemid", SqlDbType.Int);
cmdfindsystemid.Parameters["#systemid"].Direction = ParameterDirection.Output;
cmdfindsystemid.ExecuteNonQuery();
int systemid = Convert.ToInt32(cmdfindsystemid.Parameters["#systemid"].Value);
Your SQL Server Statement should look like this. Here #IsSuccess is used as the output parameter in SQL Server.
Note: There may be different ways of assigning the value into the parameter #IsSuccess as I have assigned -1, 1 and 2 as per the logic.
You can try this if your primary column is the identity key also.
SET #IsSuccess = (SELECT SCOPE_IDENTITY())
You can also use the The OUTPUT Clause for INSERT and DELETE Statements.
To call this in C# you can follow this previous answer.
CREATE PROCEDURE [dbo].[AddEditTax] #TaxCode INT
,#TaxName VARCHAR(40)
,#TaxValue NUMERIC(18, 3)
,#IsActive BIT
,#UserCode BIGINT
,#IsSuccess AS INT OUTPUT
AS
BEGIN
DECLARE #tempCode BIGINT
SET #IsSuccess = 0
IF (#TaxCode <= 0) -- ADD
BEGIN
SET #tempCode = (
SELECT ISNULL(MAX(TaxCode), 0) + 1 AS TaxCode
FROM Tax_Mst
)
IF (
NOT EXISTS (
SELECT *
FROM Tax_Mst
WHERE TaxName = #TaxName
)
)
BEGIN
INSERT INTO Tax_Mst (
TaxCode
,TaxValue
,TaxName
,IsActive
,CreatedBy
,CreatedDate
)
VALUES (
#tempCode
,#TaxValue
,#TaxName
,#IsActive
,#UserCode
,CONVERT(VARCHAR, getdate(), 109)
)
SET #IsSuccess = 1
END
ELSE
BEGIN
SET #IsSuccess = - 1
END
END
ELSE IF (#TaxCode > 0) -- Update
BEGIN
IF (
NOT EXISTS (
SELECT *
FROM Tax_Mst
WHERE TaxName = #TaxName
AND TaxCode <> #TaxCode
)
)
BEGIN
UPDATE Tax_Mst
SET TaxName = #TaxName
,TaxValue = #TaxValue
,ModifyBy = #UserCode
WHERE TaxCode = #TaxCode
SET #IsSuccess = 1
END
ELSE
BEGIN
SET #IsSuccess = 2
END
END
END
I use the below method to call the above procedure in C# class.
public static int AddEditTax(Tax objTax)
{
int retVal = 0;
DbCommand objdbCmd;
try
{
String strDBName = BaseClass.GetDatabaseName();
Database ObjDB;
ObjDB = DatabaseFactory.CreateDatabase();
objdbCmd = ObjDB.GetStoredProcCommand("AddEditTax");
if (objTax.TaxCode.HasValue)
ObjDB.AddInParameter(objdbCmd, "TaxCode", DbType.Int32, objTax.TaxCode);
else ObjDB.AddInParameter(objdbCmd, "TaxCode", DbType.Int64, DBNull.Value);
ObjDB.AddInParameter(objdbCmd, "TaxName", DbType.String, objTax.TaxName);
ObjDB.AddInParameter(objdbCmd, "TaxValue", DbType.Decimal, objTax.TaxValue);
ObjDB.AddInParameter(objdbCmd, "IsActive", DbType.Boolean, objTax.Status);
ObjDB.AddInParameter(objdbCmd, "UserCode", DbType.Int64, objTax.CreatedBy.Value);
ObjDB.AddParameter(objdbCmd, "IsSuccess", DbType.Int32, ParameterDirection.Output, "", DataRowVersion.Default, null);
ObjDB.ExecuteNonQuery(objdbCmd);
if (ObjDB.GetParameterValue(objdbCmd, "IsSuccess") != DBNull.Value)
retVal = Convert.ToInt32(ObjDB.GetParameterValue(objdbCmd, "IsSuccess").ToString());
}
catch (Exception objEx)
{
ErrorLogDAL objErrorLogManager = new ErrorLogDAL();
objErrorLogManager.AddErrorLog(objEx);
objErrorLogManager = null;
}
finally
{
objdbCmd = null;
}
return retVal;
}
The value of output parameter in page can be used like this.
int isSuccess = Convert.ToInt32(TaxDAL.AddEditTax(objTax));
if (isSuccess == 1)
{
ScriptManager.RegisterStartupScript(this, typeof(Page), "Alert1", "alert('Record saved sucessfully.');", true);
return;
}
else if (isSuccess == 2)
{
PMessage.InnerHtml = "Record already exists";
PMessage.Attributes.Add("class", "errorMessageText");
PMessage.Visible = true;
return;
}
else
{
PMessage.InnerHtml = "Record not saved sucessfully";
PMessage.Attributes.Add("class", "errorMessageText");
PMessage.Visible = true;
return;
}
You can refere this link for the reference.

How can I get the Last Index of mysql that has been inserted to C# code behind?

So I have a MySql stored procedure for inserting a row:
DELIMITER $$
CREATE PROCEDURE AddPosition(
IN strName VARCHAR(1000)
,IN strPositionType VARCHAR(1000)
,OUT intPositionId INT
)
BEGIN
INSERT INTO positions (
positionName
, positionType
)
VALUES(
strName
,strPositionType
);
SET intPositionId = LAST_INSERT_ID();
END
Honestly, I'm not really sure if this SET intPositionId = LAST_INSERT_ID(); works, I want to get the value of IntPositionId and bring it to C#
in my c#;
public bool AddItem(Position data)
{
int newId = 0;
MySqlCommand cmd = new MySqlCommand();
cmd.CommandText = "AddPosition";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("strName", data.PositionName).Direction = ParameterDirection.Input;
cmd.Parameters.AddWithValue("strPositionType", data.PositionType).Direction = ParameterDirection.Input;
cmd.Parameters.AddWithValue("intPositionId", SqlDbType.Int).Direction = ParameterDirection.Output;
try
{
MyHelper.MyExecuteNonQuery(cmd);
newId = (int) cmd.Parameters["intPositionId"].Value;
return true;
}
catch (Exception e)
{
MessageBox.Show(e.Message);
return false;
}
}
The newId = (int) cmd.Parameters["intPositionId"].Value; says "Specified cast is not valid."
when I tried to use debugging to find out what cmd.Parameters["intPositionId"].Value; returns,
it is just plain object with no value. I guess the SET intPositionId = LAST_INSERT_ID(); didnt work.
so where did I go wrong here?

stored procedure expect some input though its provided

I am stuck in a problem. i am getting error "Procedure or function 'SP_RPT_User' expects parameter '#deptName', which was not supplied." in c# application while parameter is provided. even i copied and replaced the name. still no success.
public DataTable SP_RPT_User(int loggedid, String deptName, String OfficeName, String empType)
{
int updatedrows = 0;
DataTable table = new DataTable();
try
{
cCommand = new System.Data.SqlClient.SqlCommand("SP_RPT_User", connection);
cCommand.CommandType = CommandType.StoredProcedure;
cCommand.Parameters.Add("#loggedId", SqlDbType.Int).Value = loggedid;
cCommand.Parameters.Add("#deptName", SqlDbType.NVarChar, 200).Value = deptName;
cCommand.Parameters.Add("#OfficeName", SqlDbType.VarChar, 150).Value = OfficeName;
cCommand.Parameters.Add("#empType", SqlDbType.VarChar, 150).Value = empType;
cCommand.CommandTimeout = 90000;
connection.Open();
updatedrows = cCommand.ExecuteNonQuery();
using (var da = new SqlDataAdapter(cCommand))
{
cCommand.CommandType = CommandType.StoredProcedure;
da.Fill(table);
}
}
catch (Exception Ex)
{
connection.Close();
// return -100;
}
finally
{
connection.Close();
}
return table;
}
Stored Procedure
ALTER PROCEDURE [dbo].[SP_RPT_User]
-- Add the parameters for the stored procedure here
#loggedId int,
#deptName NVarChar(200),
#OfficeName varchar(150),
#empType varchar(150)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
declare #sql nvarchar(max);
set #sql ='SELECT ...'; // here is one query
if(#deptName != '')
set #sql = #sql + ' and dbo.TB_Department.name like ''%'+#deptName+'%''';
Declare #params nvarchar(500)
SELECT #params ='#loggedId int,'+
'#deptName NVarChar(200),'+
'#OfficeName varchar(150),'+
'#empType varchar(150)'
exec sp_executesql #sql, #params,#loggedId,#deptName,#OfficeName,#empType;
END
Can anyone help. thanks in advance.
i am using sql server 2014 and vs2015.
The educated guess I have is that deptName value in C# is null while you execute the query. In such case you should pass DBNull.Value to have null as parameter value:
var param = cCommand.Parameters.Add("#deptName", SqlDbType.NVarChar, 200);
param.Value = deptName ?? DBNull.Value;
From your procedure I see that you compare with empty string so use ?? string.Empty to satisfy that condition.

Get resultSet and ReturnedValue from procedure

I need to get resultSet and ReturnedValue from procedure.
My problem is that as i understand to get the returned value i need to execute the "ExecuteNonQuery()" and for the reader i need to execute "ExecuteReader()".
But this doesnt seems right that i call execute 2 times.
What is the currect way of doing so
using (OleDbConnection connection = new OleDbConnection(getConnetionString()))
{
using (OleDbCommand command = new OleDbCommand())
{
command.CommandText = "GamesApp.Images_Get";
command.Connection = connection;
command.CommandType = CommandType.StoredProcedure;
OleDbParameter retParameter = DBUtils.createReturnOleParam(OleDbType.Integer);
command.Parameters.Add(retParameter);
tmpParameter = DBUtils.createDBParam("#ImageID", OleDbType.Integer, ParameterDirection.Input, id);
command.Parameters.Add(tmpParameter);
command.Connection.Open();
command.ExecuteNonQuery();
int retValue = (int) retParameter.Value;
if (retValue == 0)
using (OleDbDataReader reader = command.ExecuteReader())
{
if (reader != null)
{
while (reader.Read())
{
Debug.WriteLine(reader.GetString(1));
}
reader.Close();
}
}
connection.Close();
}
}
GamesApp.Images_Get":
ALTER PROCEDURE [GamesApp].[Images_Get]
#ImageID SMALLINT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #ErrorProc VARCHAR(500), #ErrorNumber INT, #ErrorSeverity INT, #ErrorState INT, #ErrorLine INT, #ErrorMessage VARCHAR(512)
DECLARE #ProcParams NVARCHAR(4000);
BEGIN TRY
--Check if #ImageID exists.if not error code is 1
IF NOT EXISTS (SELECT * FROM GamesApp.Images WHERE ImageID = #ImageID)
RETURN 1
SELECT ImageName, BG_Color, DisplayText, Text_Color, Text_Align, Text_Size
, ImageFormat, HtmlImageType, TheImage
FROM GamesApp.Images
WHERE ImageID = #ImageID;
RETURN 0;
END TRY
BEGIN CATCH
SELECT
#ErrorNumber = ERROR_NUMBER(),
#ErrorSeverity = ERROR_SEVERITY(),
#ErrorState = ERROR_STATE(),
#ErrorLine = ERROR_LINE(),
#ErrorMessage = ERROR_MESSAGE(),
#ErrorProc = 'GamesApp.Images_Get',
#ProcParams = '#ImageID' + #ImageID
EXEC dbo.Log_Exceptions_Insert #ErrorProc, #ProcParams, #ErrorLine, #ErrorNumber, #ErrorSeverity, #ErrorState, #ErrorMessage;
THROW;
END CATCH
END
I think there is no need to call ExecuteNonQuery()
As mentioned by #Damith you can use OleDbDataReader.HasRows
For verification you can look here and here

Categories

Resources