Getting stored procedure output parameter with LINQ and Entity Framework - c#

I've created a stored procedure that takes parameters to create a user. If the user already exists it sets the output parameter to 'User already exists' and does nothing more.
Now I've mapped this function (InsertNewUser) to my Entity Framework and am calling it like so:
context.InsertNewUser(email, name, passwordhash, salt, ???)
The ??? is where I'm having trouble. In the stored procedure this parameter is an OUTPUT parameter. I tried declaring a string and then passing in "out declaredString" but that wasn't correct.
I'm not sure I'm going about this the right way, any thoughts?
This is the Stored Procedure:
ALTER PROCEDURE dbo.InsertNewUser
(
#eMail nvarchar(256),
#firstName nvarchar(256),
#lastName nvarchar(256),
#passwordHash nvarchar(256),
#salt nvarchar(256),
#output nvarchar(256) OUTPUT
)
AS
/* Saves a user to the db. */
BEGIN
--First check if the user doesn't exist
IF EXISTS (SELECT eMail FROM UserSet WHERE eMail = #eMail)
--Return that user exists
SET #output = 'User exists'
ELSE
INSERT INTO UserSet
VALUES (#eMail, #firstName, #lastName, #passwordHash, #salt)
END

You can also write in the following way:
string output = "";
context.InsertNewUser(email, name, passwordhash, salt, ref output)

I solved it with this code:
//This will provide the parameter
System.Data.Objects.ObjectParameter parameter = new ObjectParameter("output", "nvarchar(256)");
//This will contain the returned values
ObjectResult result = context.CheckIfUserExists(eMail, parameter);

I solved it with following codeļ¼š
//Execute stored procedure
ObjectParameter newkey = new ObjectParameter("newKey", typeof(char));
db.RF_GetNewKey("A", "B", "S",newkey);
//get new key output from stored procedure RF_GetNewKey
string myKey=newkey.Value.ToString();
With Entity Framework 6

Related

SQL Server stored procedure hashes differently in C# to Management Studio

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.

Initializing parameters in a stored procedure

I am getting an exception when executing a stored procedure:
Procedure or function 'spAddItemByUrl' expects parameter '#userId', which was not supplied.
But I have initialized it, so what I'm dong wrong?
Here is the stored procedure:
create proc spAddItemByUrl
#title nvarchar(50),
#body nvarchar(50),
#link nvarchar(50),
#userName nvarchar(50),
#url nvarchar(50),
#userId int,
#feedId int
as
begin
insert into feed(title, body, link) values(#title, #body, #link)
select #userId = id from users where name = #userName
select #feedId = id from feed where title = #title
insert into userstofeed(userid, feedid) values(#userId, #feedId)
insert into feedurl(url, feedid, userid) values(#url, #feedId, #userId)
end
And here's the C# code.
The exception is thrown when I call ExecuteNonQuery:
connection.SqlCommand.Parameters.AddWithValue("#url", urlFeed.Url);
connection.SqlCommand.Parameters.AddWithValue("#title", item.Title.Text);
connection.SqlCommand.Parameters.AddWithValue("#body", item.Summary.Text);
connection.SqlCommand.Parameters.AddWithValue("#link", item.Id);
connection.SqlCommand.Parameters.AddWithValue("#userName", user);
connection.SqlCommand.ExecuteNonQuery();
connection.SqlCommand.Parameters.Clear();
You've added #userId int, #feedId int to the list of parameters for your SProc, but from the query it looks like you don't want them to be provided by the user. You can declare them inside the SProc to make them "local".
i.e.
create proc spAddItemByUrl
#title nvarchar(50),
#body nvarchar(50),
#link nvarchar(50),
#userName nvarchar(50),
#url nvarchar(50)
as
begin
DECLARE #userId int, #feedId int;
insert into feed(title, body, link) values(#title, #body, #link)
select #userId = id from users where name = #userName
select #feedId = id from feed where title = #title
insert into userstofeed(userid, feedid) values(#userId, #feedId)
insert into feedurl(url, feedid, userid) values(#url, #feedId, #userId)
end
You've specified
connection.SqlCommand.Parameters.AddWithValue("#userName", user);
but not #userid
If you want to add default values to your stored procedure arguments you can do the following:
create proc spAddItemByUrl
...
#userId int = 0, /* or whatever you want the default value to be */
#feedId int = 0
If you decide to call your stored procedure, you then only have to pass the five arguments provided in your code, and the values #userId and #feedId will be 0. If you decide to specify values for those two parameters in your calling function, then the values you pass will be used.
Since your stored procedure doesn't use those parameters though, you should remove them from the section they are in.
Instead just do
declare #userId int = select id from users where name = #userName
declare #feedId int = select id from feed where title = #title

Conversion failed when converting from a character string to uniqueidentifier. in C# code when I try to insert record by SQL insert statement

I have a method in my C# code that insert record in SQL table , I am getting this error right when I execute insert query:
"Conversion failed when converting from a character string to uniqueidentifier."
This is line I have in my C# code that produce error AgencyId and User are both uniqueidentifier in SQl table.
db.AddInParameter(command, Agency, DbType.String, dto.AgencyId);
db.AddInParameter(command, User, DbType.String, dto.User);
This is my insert Stored proc:
ALTER PROCEDURE [dbo].[insert_ApplicantLoan]
#RemRef varchar(15) = null
,#Agency varchar(100) = null /* uniqueidentifier */
,#User varchar(100) = null /* uniqueidentifier */
AS
BEGIN
INSERT INTO [dbo].[ApplicantLoan]
(
[RemRef]
,[_Agency]
,[_User]
)
VALUES
(
#RemRef
,#Agency
,#User)
Set #Id = SCOPE_IDENTITY()
Select #Id
End
END
Well, not sure if I misread something, but it looks like your data type in the DB is supposed to be uniqueidentifier. That means that you'll have to update your SP to accept parameters of that type, instead of varchar, as it is now.

Stored procedure/query two variable comparison

I have created a stored procedure for SQL Server 2014.
There are two parameters: Name which is a user name and Hash which is password md5 hash. I check in the database if the md5 hashes are equal (first hash is from the program and the second one is already stored in the database).
If I just run a query (not a stored procedure) in the database (or in program using commandType.Text) - everything works and the user is being selected, but when I run the exact thing but using stored procedures, the SqlReader in C# has no elements returned, which most likely means that the conditions during those variable comparison were not met.
Maybe I am doing something wrong?
I also have about 10 other stored procedures for reading or/and writing to the database, everything works except this one.
Here is the procedure:
CREATE PROCEDURE GetHash
#Name nvarchar(50),
#Hash nvarchar(200)
AS
BEGIN
SET NOCOUNT ON;
SELECT Orders.orderId, Employee.name, Employee.surname
FROM Orders
LEFT JOIN Employee ON Orders.orderId = Employee.id
WHERE batchName = '#Name' AND productCode = '#Hash'
END
GO
Code part:
public Boolean VerifyPassword(string name, string password)
{
var paramsList = new List<SqlParameter> { new SqlParameter("#Name", name), new SqlParameter("#Hash", GetMd5Hash(password)) };
const string ProcedureName = "GetHash";
var ActiveUser = new DBController().GetFromDatabase(ProcedureName, "Login", "EJL15_DB", paramsList).ToList();
return ActiveUser.Count > 0;
}
And from Database Controller
private void SetCommandProperties(string procedureName, IEnumerable<SqlParameter> paramsList)
{
this.sqlCommand.CommandText = procedureName;
this.sqlCommand.CommandType = CommandType.StoredProcedure;
foreach (var curParam in paramsList)
this.sqlCommand.Parameters.Add(curParam);
this.sqlCommand.CommandTimeout = 15;
}
You don't need to quote the parameters in the stored procedure. Do this instead:
CREATE PROCEDURE GetHash
#Name nvarchar(50),
#Hash nvarchar(200)
AS
BEGIN
SET NOCOUNT ON;
SELECT Orders.orderId,
Employee.name,
Employee.surname
FROM Orders
LEFT JOIN Employee
ON Orders.orderId=Employee.id
WHERE batchName = #Name
AND productCode = #Hash
END
I just wonder, obviously your #Hash parameter passed to the stored
procedure is a user's password. But for some reason your WHERE clause
in the procedure goes like that:
"WHERE batchName='#Name' AND productCode='#Hash'"
Is there a chance your condition is incorrect? I guess it should be something like: Employee.password = #Hash
You should not put '' around your variables. Otherwise your comparison is totally wrong.

C#/SQL get autoincremented field value

I have a table with autoincremented primary key. In my code I am trying to receive the new autoincremented value when I execute each 'insert' query. Is there a way to do it programatically?
Thanks.
UPD:
Assume I have a table:
TABLE User ( userID INT NOT NULL AUTO_INCREMENT, name VARCHAR( 25 ) NOT NULL , email VARCHAR( 50 ) NOT NULL , UNIQUE ( userID ) );
And I when I insert new values (name and email) to this table I want automatically receive newly generated userID. Ideally I am looking for any ways to do that with a single transaction and without stored procedures.
Have your sql/stored proc return scope_identity() or if you are using Linq2SQL or EF the entity used for insertion gets the new id.
In the stored proc it is:
ALTER proc [dbo].[SaveBuild](
#ID int = 0 output,
#Name varchar(150)=null,
#StageID int,
#Status char(1)=null
)
as
SET NOCOUNT ON
Insert into Builds
(name, StageID, status)
values (#Name, #StageID, #Status)
select #ID = scope_identity()
RETURN #ID
In the C# code you have:
public int SaveBuild(ref int id, ref string Name)
{
SqlCommand cmd = GetNewCmd("dbo.SaveBuild");
cmd.Parameters.Add("#ID", SqlDbType.Int).Value = id;
cmd.Parameters["#ID"].Direction = ParameterDirection.InputOutput;
cmd.Parameters.Add("#Name", SqlDbType.VarChar).Value = Name;
cmd.Parameters.Add("#StageID", SqlDbType.Int).Value = 0;
ExecuteNonQuery(cmd);
id = (int)cmd.Parameters["#ID"].Value;
return id;
}
Dependent upon your situation, you might be better off using table-valued parameters to pass your inserts to a stored procedure, then use OUTPUT INSERTED to return a table-valued parameter from your stored procedure.
It will drastically reduce the number of hits required if you're processing multiple items.
Are you limited to building SQL on the client and sending it to the server? Cause if you can use a stored procedure, this is easy to do. In the stored proc, do the insert and then, either
Select Scope_Identity() as the last statement in the stored proc., or
Use a output parameter to the stored proc, (say named #NewPKValue) and make the last statement:
Set #NewPKValue = Scope_Identity()
Otherwise, you need to send a batch of commands to the server that include two statements, the insert, and Select Scope_Identity() and execute the batch as though it was a select statement
You could use the SQL statement SELECT scope_identity().

Categories

Resources