ExecuteNonQuery on a stored proc causes it to be deleted - c#

This is a strange one. I have a Dev SQL Server which has the stored proc on it, and the same stored proc when used with the same code on the UAT DB causes it to delete itself!
Has anyone heard of this behaviour?
SQL Code:
-- Check if user is registered with the system
IF OBJECT_ID('dbo.sp_is_valid_user') IS NOT NULL
BEGIN
DROP PROCEDURE dbo.sp_is_valid_user
IF OBJECT_ID('dbo.sp_is_valid_user') IS NOT NULL
PRINT '<<< FAILED DROPPING PROCEDURE dbo.sp_is_valid_user >>>'
ELSE
PRINT '<<< DROPPED PROCEDURE dbo.sp_is_valid_user >>>'
END
go
create procedure dbo.sp_is_valid_user
#username as varchar(20),
#isvalid as int OUTPUT
AS
BEGIN
declare #tmpuser as varchar(20)
select #tmpuser = username from CPUserData where username = #username
if #tmpuser = #username
BEGIN
select #isvalid = 1
END
else
BEGIN
select #isvalid = 0
END
END
GO
Usage example
DECLARE #isvalid int
exec dbo.sp_is_valid_user 'username', #isvalid OUTPUT
SELECT valid = #isvalid
The usage example work all day... when I access it via C# it deletes itself in the UAT SQL DB but not the Dev one!!
C# Code:
public bool IsValidUser(string sUsername, ref string sErrMsg)
{
string sDBConn = ConfigurationSettings.AppSettings["StoredProcDBConnection"];
SqlCommand sqlcmd = new SqlCommand();
SqlDataAdapter sqlAdapter = new SqlDataAdapter();
try
{
SqlConnection conn = new SqlConnection(sDBConn);
sqlcmd.Connection = conn;
conn.Open();
sqlcmd.CommandType = CommandType.StoredProcedure;
sqlcmd.CommandText = "sp_is_valid_user";
// params to pass in
sqlcmd.Parameters.AddWithValue("#username", sUsername);
// param for checking success passed back out
sqlcmd.Parameters.Add("#isvalid", SqlDbType.Int);
sqlcmd.Parameters["#isvalid"].Direction = ParameterDirection.Output;
sqlcmd.ExecuteNonQuery();
int nIsValid = (int)sqlcmd.Parameters["#isvalid"].Value;
if (nIsValid == 1)
{
conn.Close();
sErrMsg = "User Valid";
return true;
}
else
{
conn.Close();
sErrMsg = "Username : " + sUsername + " not found.";
return false;
}
}
catch (Exception e)
{
sErrMsg = "Error :" + e.Source + " msg: " + e.Message;
return false;
}
}

Ok, I have found the answer ... simple when you know how!
I saw this link here :
Disappearing Stored Procedure
Disappearing Stored Procedure
So from the best answer in that I ran :
select syo.name
from syscomments syc
join sysobjects syo on
syo.id = syc.id
where syc.[text] like '%DROP PROC%'
This gave me one of my OTHER stored procs back... sp_is_user_admin, which didn't seem right so I had a quick look ...
create procedure dbo.sp_is_user_admin
#username as varchar(20),
#isadmin as int OUTPUT
AS
BEGIN
declare #profile as varchar(20)
select #profile = profile from CPUserData where username = #username
if #profile = 'admin'
BEGIN
select #isadmin = 1
END
else
BEGIN
select #isadmin = 0
END
END
--*********************************************************************************
-- Check if user is registered with the system
IF OBJECT_ID('dbo.sp_is_valid_user') IS NOT NULL
BEGIN
DROP PROCEDURE dbo.sp_is_valid_user
IF OBJECT_ID('dbo.sp_is_valid_user') IS NOT NULL
PRINT '<<< FAILED DROPPING PROCEDURE dbo.sp_is_valid_user >>>'
ELSE
PRINT '<<< DROPPED PROCEDURE dbo.sp_is_valid_user >>>'
END
Doh!!! There is the blighter... in the C# what happens is that if the user is valid I also choose what to let them see based on if they are admin or not and calling that was blitzing the sp_is_valid_user proc. Nasty side effect!
// check the user is entitled to use the system at all
if (usrData.IsValidUser(sCurrentUserName, ref sErrMsg))
{
// if the user is admin then let them spoof and edit their own data
if (usrData.UserIsAdmin(sCurrentUserName, ref sErrMsg))
{
chkSpoof.Visible = true;
grdvwUserDataFromDB.Visible = true;
}
}
else
{
// redirect them away
Response.Redirect("UserNotRegistered.aspx");
return;
}
I hope this helps someone else out!
PS: DB Artisan is nasty and if I had the full fat SQL Server available in my Development toolkit then I guess I could have used the profiler to see this being called. ;P I can't install SQL Server 2008 as I don't have the right SP / updates to Visual Studio I think and IT here can't sort it out, annoying!!

Related

Can't call stored procedure

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>(/.../)

Conversion error when i call a stored procedure from SQL Server in C#

I have a stored procedure called lastID like this:
CREATE PROCEDURE lastID(#id varchar(64) OUTPUT)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #f VARCHAR(64);
SELECT TOP 1 #f = work_id
FROM workorder
WHERE (RIGHT(work_id,2)) = (RIGHT(Year(getDate()),2))
ORDER BY work_id DESC;
IF(#f IS NULL)
BEGIN
SET #f = 'No work orders';
SET #id = #f;
RETURN #id;
END
ELSE
BEGIN
SET #id = #f;
RETURN #id;
END
END
This stored procedure returns the last id from the table workorder, now I'm trying to execute this procedure in C#, this is the code:
private void lastWorkId()
{
String strConnString = "Server=.\\SQLEXPRESS;Database=recalls;Integrated Security=true";
SqlConnection con = new SqlConnection(strConnString);
SqlCommand cmd = new SqlCommand();
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "lastID";
cmd.Parameters.Add("#id", SqlDbType.VarChar, 64).Direction = ParameterDirection.Output;
cmd.Connection = con;
try
{
con.Open();
cmd.ExecuteNonQuery();
String id = cmd.Parameters["#id"].Value.ToString();
lastid.Text = id.ToString(); //Putting the return value into a label
}
catch (Exception ex)
{
throw ex;
}
finally
{
con.Close();
con.Dispose();
}
}
I don't know what are wrong with my code, because an exception is displayed, and this says
Conversion failed when converting the varchar value ' OT- 003-16 ' to data type int
I was wrong about my first answer, here is the updated answer:
Your stored procedure is setup with an OUTPUT parameter of type VARCHAR(64).
Within your proc you have a couple of RETURN #id; statements, which is actually returning a VARCHAR(64). You only need to set your OUTPUT variable within the stored procedure. The RETURN statement expects an integer expression. Here's the updated fixed sproc using OUTPUT appropriately:
ALTER PROCEDURE [dbo].[lastID](#id varchar(64) OUTPUT)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #f VARCHAR(64);
SELECT TOP 1 #f = work_id FROM workorder WHERE (RIGHT(work_id,2)) = (RIGHT(Year(getDate()),2)) ORDER BY work_id DESC;
IF(#f IS NULL)
BEGIN
SET #f = 'No work orders';
SET #id = #f;
END
ELSE
BEGIN
SET #id = #f;
END
END
Error is basically should get fixed by cast
((RIGHT(work_id,2)) as int)
But code can be further condensed and improved.
CREATE PROCEDURE lastID(#id varchar(64) OUTPUT)
AS
BEGIN
SET NOCOUNT ON;
SELECT TOP 1 #id = isnull(work_id , 'No work orders') FROM workorder WHERE cast ((RIGHT(work_id,2)) as int)= (RIGHT(Year(getDate()),2)) ORDER BY work_id DESC;
RETURN #id;
END

how to write stored procedure with two different queries

Actually i am a beginner in database ..
I have written a stored procedure in which I want to get results in c# winform app from two different tables using if else , for instance I have two tables with one column in common that is 'comp_number' .. now I have written a stored procedure which executes on a button click event
ALTER procedure [dbo].[complainVehicle_sp]
as
DECLARE #compno int
if #compno is not null
begin
select compno,compdate,regno,engineno,mcode from dbo.complainVehicle
where compno = #compno
end
else
begin
select compno,recoverydt,recoverytime,statuscode from dbo.complainRecovery
where compno = #compno
end
Now I want that if Compno matches table complainVehicle it shows me the result against this , and if it matches with table complainRecovery it shows me the result against that record else it will display no record ..
here is my c# code
string str = #"Data Source=.;Initial Catalog=----;Integrated Security=False;User ID=sa;Password=----;Connect Timeout=15;Encrypt=False;TrustServerCertificate=False;";
SqlConnection cnn = null;
try
{
cnn = new SqlConnection(str);
cnn.Open(); //open the connection
}
catch (SqlException err)
{
Console.WriteLine("Error: " + err.ToString());
}
finally
{
if (cnn != null)
{
cnn.Close();
}
}
if (textBox1.Text.Trim().Length == 0)
{MessageBox.Show("No Record");}
else if (textBox1.Text.Trim().Length > 0)
{
cnn.Open();
SqlCommand cmd2 = new SqlCommand();
cmd2.Connection = cnn;
cmd2.CommandType = CommandType.StoredProcedure;
cmd2.CommandText = "complainVehicle_sp";
cmd2.Parameters.Add("#compno", System.Data.SqlDbType.NVarChar).Value = textBox1.Text.ToString();
cmd2.ExecuteNonQuery();
SqlDataAdapter da = new SqlDataAdapter(cmd2);
DataSet ds = new DataSet();
da.Fill(ds);
dataGridView1.DataSource = ds.Tables[0];
cnn.Close();
As I write compno in textbox and click sumbit it shows an error
`An unhandled exception of type 'System.Data.SqlClient.SqlException' occurred in System.Data.dll
Additional information: Procedure complainVehicle_sp has no parameters and arguments were supplied.`
... i would appreciate you all for this help .. thanks in advance guys
In your code you are using DECLARE #compno int, which creates a local variable within the procedure body. The #compno variable is not accessible from outside of the stored procedure context, and it means nothing to the C# code that is invoking the procedure:
cmd2.Parameters.Add(
"#compno",
System.Data.SqlDbType.NVarChar).Value = textBox1.Text.ToString();
So, to address your issue, first, change the stored procedure to accept parameters.
ALTER PROCEDURE [dbo].[complainVehicle_sp]
-- declare a parameter #compono to the procedure
#compno INT
as ...
BEGIN
IF #compno IS NOT NULL
BEGIN
SELECT compno,compdate,regno,engineno,mcode
FROM dbo.complainVehicle
WHERE compno = #compno
END
ELSE
BEGIN
SELECT compno,recoverydt,recoverytime,statuscode
FROM dbo.complainRecovery
WHERE compno = #compno
END
END
Second, you must add the appropriate parameter type in your C# code:
cmd2.Parameters.Add(
"#compno",
System.Data.SqlDbType.Int).Value = int.Parse(textBox1.Text);
Since the parameter is declared as INT in the stored procedure definition, you need to use System.Data.SqlDbType.Int and provide a valid integer value by calling int.Parse(textBox1.Text).
Refer to T-SQL Stored Procedure Syntax for more information on creating stored procedures and parameter options
Firstly, you have to declare your procedure with a parameter, then you probably want to use EXISTS to check each table, something like this;
alter procedure [dbo].[complainVehicle_sp] (#compno int)
as
if (exists (select 1 from dbo.complainVehicle where compno = #compno ) )
begin
select compno,compdate,regno,engineno,mcode from dbo.complainVehicle
where compno = #compno
end
else
if (exists (select 1 from dbo.complainRecovery where compno = #compno ) )
begin
select compno,recoverydt,recoverytime,statuscode from dbo.complainRecovery
where compno = #compno
end
You also need to specify the type of the parameter correctly;
cmd2.Parameters.Add("#compno", System.Data.SqlDbType.Int).Value = textBox1.Text.ToString();

ExecuteScalar() method is not working properly

I have the following function;
public int GetLoginClaim(IsValidLogin Obj)
{
SqlConnection DBCon = new SqlConnection(ConString);
SqlCommand CmdSelect = new SqlCommand("IsValidLogin", DBCon);
CmdSelect.CommandType = CommandType.StoredProcedure;
DBCon.Open();
try
{
CmdSelect.Parameters.AddWithValue("#UserName", Obj.Username);
CmdSelect.Parameters.AddWithValue("#Password", Obj.Password);
return (int)CmdSelect.ExecuteScalar();
}
catch
{
throw;
}
finally
{
CmdSelect.Dispose();
DBCon.Close();
DBCon.Dispose();
}
}
And the following stored procedure on which it depends;
USE [SMania]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[IsValidLogin]
#UserName varchar(32),
#Password varchar(32)
As
if exists(Select * From NewClientTB Where UserName = #UserName And Password = #Password)
return(1)
else
return(0)
The stored procedure is working correctly when executed on sql server, but when called from the function i posted above, it gives error. The above function is in my data access layer. So in the stack trace, i am having the following error on the above function:
NullReferenceException: Object reference not set to an instance of an object. Can anyone fix this problem?
You need different approach for SPs with return status
Check the example on this MSDN article http://msdn.microsoft.com/en-us/library/ms378371(v=sql.90).aspx
If you want to use ExecuteScalar you need to replace return(X) with select X in SP because it "returns the first column of the first row in the result set" but there is no result set in your SP only return value.
This is how you can get return status in C#
CmdSelect.Parameters.AddWithValue("#UserName", Obj.Username);
CmdSelect.Parameters.AddWithValue("#Password", Obj.Password);
var return_state = CmdSelect.Parameters.Add("#ReturnVal", SqlDbType.Int);
return_state.Direction = ParameterDirection.ReturnValue;
CmdSelect.ExecuteNonQuery();
return (int)return_state.Value;

Catch the error from Stored Procedure in C#

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

Categories