I have the following Stored procedure
ALTER PROCEDURE dbo.USER_AUTH
(
#username varchar,
#password varchar
)
AS
BEGIN
SELECT * FROM users
WHERE username = #username
AND password = #password;
END
And there are Two rows in the users table.
This gives an empty reader error
_Command = new SqlCommand("[dbo].[USER_AUTH]", _Connection);
_Command.CommandType = System.Data.CommandType.StoredProcedure;
_Command.Parameters.AddWithValue("#username", _Username);
_Command.Parameters.AddWithValue("#password", _Password);
reader = _Command.ExecuteReader();
While this works fine
_Command = new SqlCommand("select * from users where
username=#username and password=#password" , _Connection);
_Command.Parameters.AddWithValue("#username", _Username);
_Command.Parameters.AddWithValue("#password", _Password);
reader = _Command.ExecuteReader();
Where _Connection is SqlConnection _Connection , _Command is SqlCommand _Command and
reader is SqlDataReader
This happens because your stored procedure declares
ALTER PROCEDURE dbo.USER_AUTH
(
#username varchar,
#password varchar
)
without specifying a size for the two varchars. In this way just one char is passed to the parameters and of course nothing is retrieved
Change the sp to
ALTER PROCEDURE dbo.USER_AUTH
(
#username nvarchar(30),
#password nvarchar(30)
)
or whatever size are your two database fields
You have to specify the length for varchar
So if we don't specify the length ourself these are the default values SQL Server uses - which means the data what we would be expecting to get stored in the database would have got silently truncated without our knowledge.
Bad habits to kick : declaring VARCHAR without (length)
ALTER PROCEDURE dbo.USER_AUTH
(
#username varchar(20),
#password varchar(20)
)
AS
BEGIN
SELECT * FROM users
WHERE username = #username
AND password = #password;
END
Related
I'm trying to make an insert from my controller to my database. I already debug and it never enter on the exception. But i can not see the values on the db.
This is my sp:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[RegisterUser]
-- Add the parameters for the stored procedure here
#Username nvarchar(10),
#Password nvarchar(10),
#Mail nvarchar(10),
#Birthday date
AS
BEGIN
SET NOCOUNT ON
INSERT INTO Users
(
Username,
Pass,
Mail,
Birthday
)
VALUES
(
#Username,
#Password,
#Mail,
#Birthday
)
END
And on my model i used this method:
public bool registerUser(UserModel user)
{
bool isOk = false;
using (SqlConnection connection = DbConnection.OpenConnection2())
{
try
{
using (SqlCommand command = new SqlCommand("RegisterUser", connection))
{
command.CommandType = CommandType.StoredProcedure;
SqlCommandBuilder.DeriveParameters(command);
command.Parameters.Add(new SqlParameter("#UserName", SqlDbType.VarChar)).Value = user.userName.Trim();
command.Parameters.Add(new SqlParameter("#Password", SqlDbType.VarChar)).Value = user.password.Trim();
command.Parameters.Add(new SqlParameter("#Mail", SqlDbType.VarChar)).Value = user.mail.Trim();
command.ExecuteNonQuery();
}
}
catch (Exception e)
{
}
finally
{
if (connection.State.Equals(ConnectionState.Open))
{
connection.Close();
isOk = true;
}
}
}
return isOk;
}
Any ideas of why this is not working?
Thanks
Things are easy, if you use exception handling in a proper manner. You are swallowing the exception thrown by Ado.net. Follow these steps for solving your problem:
First of all you should add a throw statement in your catch block to get the exception being thrown. In production, You should catch that at later calling function or layer.
You need to provide all the parameters to your stored procedure. Currently, You are not passing one parameter named #Birthday and its value. Add this parameter with its value.
Here is the modified code that should work.
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add(new SqlParameter("#UserName", SqlDbType.VarChar)).Value = user.userName.Trim();
command.Parameters.Add(new SqlParameter("#Password", SqlDbType.VarChar)).Value = user.password.Trim();
command.Parameters.Add(new SqlParameter("#Mail", SqlDbType.VarChar)).Value = user.mail.Trim();
//Assuming that the user object has a field named Birthday
command.Parameters.Add(new SqlParameter("#Birthday", SqlDbType.Date)).Value = user.Birthday;
command.ExecuteNonQuery();
If you follow my first advice, you will nearly get any other problem that is occurring in your code.
I hope it will help you somehow. Thanks!
If you prefer, you can provide a default value for the parameter #Birthday as:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[RegisterUser]
-- Add the parameters for the stored procedure here
#Username nvarchar(10),
#Password nvarchar(10),
#Mail nvarchar(10),
#Birthday date = NULL
AS
BEGIN
SET NOCOUNT ON
INSERT INTO Users
(
Username,
Pass,
Mail,
Birthday
)
VALUES
(
#Username,
#Password,
#Mail,
#Birthday
)
END
If you are not supplying "#Birthday" parameter, Please specify "#Birthday date" as "#Birthday date = NULL" in the stored procedure as below:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[RegisterUser]
-- Add the parameters for the stored procedure here
#Username nvarchar(10),
#Password nvarchar(10),
#Mail nvarchar(10),
#Birthday date = NULL
AS
BEGIN
SET NOCOUNT ON
INSERT INTO Users
(
Username,
Pass,
Mail,
Birthday
)
VALUES
(
#Username,
#Password,
#Mail,
#Birthday
)
END
Rest of your code looks all good to me.
Hope this will help you.
I am calling a stored procedure to create and hash a new password. It is being called from a .NET/C# WebAPI controller.
_sqlDataContext.ExecuteCommand("[Users].[SetNewPassword]", new List<SqlParameter>
{
new SqlParameter("#UserId", SqlDbType.UniqueIdentifier) { Value = user.Id },
new SqlParameter("#Password", SqlDbType.NVarChar, 256) { Value = command.Password }
});
userId and command.Password are a GUID and simple string respectively.
The stored procedure being called hashes and stores the password as follows:
ALTER PROC [Users].[SetNewPassword]
#UserId uniqueidentifier,
#Password NVARCHAR(256) -- Unsalted and unhashed
AS
BEGIN TRANSACTION [Transaction1]
BEGIN TRY
DECLARE #PasswordSalt uniqueidentifier = NEWID();
DECLARE #SaltedPassword binary(64);
SET #SaltedPassword = HASHBYTES('SHA2_512', #Password + CAST(#PasswordSalt as NVARCHAR(256)));
-- Insert new password
INSERT INTO Passwords (UserId, Password, PasswordSalt, UpdatedDate)
VALUES (#UserId, #SaltedPassword, #PasswordSalt, GETDATE());
COMMIT TRANSACTION [Transaction1]
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION [Transaction1]
PRINT ERROR_MESSAGE()
END CATCH
When I call this from the WebAPI / C# method, I get a different hash than when I execute the stored procedure directly in SQL Server Management Studio.
Furthermore, if I execute the SetPassword stored procedure from C#, the following stored procedure - ValidatePassword - to validate a password fails.
This is because the hash stored in the database does not match the hash being generated by ValidatePassword
ALTER PROC [Users].[ValidatePassword]
#UserId uniqueidentifier,
#Password NVARCHAR(256)
AS
BEGIN
DECLARE #PasswordHash binary(64) = HASHBYTES('SHA2_512', (SELECT #Password + CAST((SELECT p.PasswordSalt FROM Passwords p WHERE p.UserId = #UserId) AS NVARCHAR(256))))
SELECT
CASE
WHEN EXISTS (SELECT UserId
FROM Passwords
WHERE UserId = #UserId
AND Password = #PasswordHash)
THEN 1
ELSE 0
END
SELECT #passwordHash;
END
Question / problem:
Why is the hash generated by the stored procedure when called from C# different to the one generated from within SQL Server Management Studio? Is there is an issue with unicode/binary conversion or something like that?
Please note I appreciate that 'rolling your own' authorisation is not always a good idea, but this is addressing a temporary issue with a legacy app.
I have a SQL procedure that I'm calling from my c# code.
cmd = new SqlCommand("InsertData", con, transaction);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#IP_No", SqlDbType.VarChar, 50);
cmd.Parameters["#IP_No"].Value = data.IP_No;
result = cmd.ExecuteNonQuery().ToString();
This is my procedure:
(
#IP_No varchar(15),
#Indent_No varchar(20)
)
AS
BEGIN
set #Indent_No = (SELECT IndentId from IdTable)
INSERT INTO InsertData(IP_No,Indent_No)
VALUES(#IP_No,#Indent_No)
Update IdTable set IndentId=IndentId+1;
END
Basically, I want my ID to be pulled from a database where I keep incrementing it.
It throws this error:
Procedure or function expects parameter #Indent_No which was not
supplied.
Please Help!
As you use #Indent_No only internally within the stored procedure, you don't need to declare it as an input parameter. You need to declare it within the body of your stored procedure
CREATE PROCEDURE InsertData
(
#IP_No varchar(15)
)
AS
BEGIN
DECLARE #Indent_No varchar(20)
set #Indent_No = (SELECT IndentId from IdTable)
INSERT INTO InsertData(IP_No,Indent_No)
VALUES(#IP_No,#Indent_No)
Update IdTable set IndentId=IndentId+1;
END
I used the following statement for a parameter.
comm.Parameters.Add("#name", SqlDbType.NVarChar, 50).Value = txtname.Text;
Name is a SQL Server nvarchar column but I get this error:
Error converting data type nvarchar to numeric.
My sql:
SqlCommand comm = conn.CreateCommand();
comm.CommandText = "execute addName #name";
comm.Parameters.Add("#name", SqlDbType.NVarChar, 50).Value = txtname.Text;
conn.Open();
comm.ExecuteNonQuery();
conn.Close();
Can help my to fix this problem?
My store procedure:
USE [info]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER procedure [dbo].[addName]
#id numeric(18,0) = 0,
#name nchar(50)
as
if (select name from TName where name = #name) is null
begin
select #id = MAX(id)+1 from TName
insert into TName
values (#id, #name)
print #id
end
else
begin
print 'Eroare'
end
With the edit, with the signature:
ALTER procedure [dbo].[addName]
#id numeric(18,0) = 0,
#name nchar(50)
then the problem becomes clear:
execute addName #name
is pass-by-position - so you are passing the value of #name into the #id parameter. If you were calling this from TSQL, to pass-by-name you need to use:
execute addName #name = #name
The first (left) states the parameter name, the second (right) states the value to use for this parameter; for example, to pass a literal into addName's #name parameter:
execute addName #name = 'Fred'
However, from ADO.NET, a better approach is:
comm.CommandText = "addName";
comm.CommandType = CommandType.StoredProcedure;
which automatically treats it as an SP-exec using pass-by-name.
You must modify the type of your parameter name in your addName procedure, and set to NVarChar
You pass in C# NVarChar type, but you have numeric who is asked in procedure
Why doesn't this work? I get an error saying the number cannot be infinity. However, I had to take this away from an insert statement so that it doesn't post entries twice.
Where do I have to incorporate this piece of code to get it to allow my code to loop as a new ID?
cmd = new SqlCommand(#"SELECT CAST(scope_identity() as int)", con);
int aID = Convert.ToInt32(cmd.ExecuteScalar());
In general you can have a stored procedure to do the INSERT and return the last inserted identity with an out parameter, as you can see in an example here: http://www.objectreference.net/post/SCOPE_IDENTITY()-return-the-id-from-the-database-on-insert.aspx
CREATE PROCEDURE [dbo].[Customer_Insert]
#Name VARCHAR(255),
#Email VARCHAR(255),
#Phone VARCHAR(255),
#CustomerID INT OUTPUT
AS
BEGIN
INSERT INTO dbo.Customer ([Name], Email, Phone)
VALUES (#Name,#Email,#Phone)
SET #CustomerID = CAST(SCOPE_IDENTITY() AS INT)
END