0

I'm trying to insert data into a database using a SQL procedure and an MVC controller. I have the data obtained using a HTML form which is then retrieved by the Create method and added to the parameters of the SQL procedure.

 public ActionResult Create([Bind(Include = "UserID,FirstName,Surname,Password,Salt,Phone_Number,Email,IsAdmin")] SaltUsersTable saltUsersTable, FormCollection fc)
        {
            if (ModelState.IsValid)
            {
                SqlConnection con = new SqlConnection(@"Connection String");
                SqlCommand cmd = new SqlCommand();

                cmd.CommandText = "dbo.AddSaltedUser";
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.Connection = con;

                cmd.Parameters.Add("@Password", SqlDbType.NVarChar, 50).Value = Request.Form["Password"];
                cmd.Parameters.Add("@FirstName", SqlDbType.NVarChar, 50).Value = Request.Form["FirstName"];
                cmd.Parameters.Add("@Surname", SqlDbType.NVarChar, 50).Value = Request.Form["Surname"];
                cmd.Parameters.Add("@Email", SqlDbType.NVarChar, 100).Value = Request.Form["Email"];
                cmd.Parameters.Add("@PhoneNumber", SqlDbType.NVarChar, 12).Value = Request.Form["PhoneNumber"];

                cmd.Parameters.Add("@response", SqlDbType.NVarChar, 250).Direction = ParameterDirection.Output;

                if (Request.Form["FirstName"] == "Admin")
                {
                    cmd.Parameters.Add("@IsAdmin", SqlDbType.Bit).Value = 1;
                }
                else
                {
                    cmd.Parameters.Add("@IsAdmin", SqlDbType.Bit).Value = 0;
                }

                //Execute the command just established
                con.Open();
                Int32 rowsAffected = cmd.ExecuteNonQuery();
                con.Close();

The code shown above is the Create controller (the connection string I've replaced to be generic)

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

 ALTER PROCEDURE [dbo].[AddSaltedUser]
    @FirstName NVARCHAR(50),
    @Surname NVARCHAR(50),
    @Password NVARCHAR(50),
    @PhoneNumber NVARCHAR(12),
    @Email NVARCHAR(50),
    @IsAdmin BIT,
    @response NVARCHAR(250) OUTPUT
AS 

BEGIN
    SET NOCOUNT ON

    DECLARE @salt UNIQUEIDENTIFIER=NEWID()
    BEGIN TRY

        INSERT INTO dbo.SaltUsersTable (FirstName, Surname , Password, PhoneNumber, Email, IsAdmin)
        VALUES(@FirstName, @Surname, HASHBYTES('SHA_512',@Password+CAST(@salt AS NVARCHAR(36))), @PhoneNumber, @Email, @IsAdmin)

        SET @response='Success'
    END TRY
    BEGIN CATCH
        SET @response='ERROR'
    END CATCH
END

This code is the SQL procedure I wrote which should insert the parameters passed from the MVC controller apart from the salted hash which is done within the procedure.

The issue with this code is that it won't actually insert anything into the database. When I run through the code and have a breakpoint at rowsAffected it shows that it is 0 and I have no idea why. Where am I going wrong in this code?

I should say however that the parameters do exist and the form values are collected properly within the controller as I have gone through it with a debugger.

Displaza
  • 75
  • 7
  • @Corey SET NOCOUNT ON does not "stop server from calculating rows affected", it stops returning it to client. With SET NOCOUNT ON you cannot get "0 rows affected", you just get nothing at all – sepupic Nov 28 '19 at 11:35
  • In your catch block add throw; it will throw the error into your client code – sepupic Nov 28 '19 at 11:45
  • How did you check whether the row was inserted or no? By querying the table or just by examine your rowsAffected? – sepupic Nov 28 '19 at 11:48
  • @sepupic I query the first 200 rows of the table and it returns one row of NULL for every column. – Displaza Nov 28 '19 at 11:51
  • This means not "it won't actually insert anything into the database" but "it inserts nulls", you should edit your question as your real question is "Why does my code insert nulls", it seems that the problem is not on the server side, it's your c# code to pass something strange – sepupic Nov 28 '19 at 12:05

2 Answers2

0

The problem is that you have a SET NOCOUNT ON statement in your stored procedure. From the docs:

Stops the message that shows the count of the number of rows affected by a Transact-SQL statement or stored procedure from being returned as part of the result set.

It does exactly what you told it to do. Comment out that line in the SP and it will return the number of rows affected: 1 in case of a successful insert, 0 if it fails.


As for your rows not inserting...

In your insert you are using HASHBYTES with an invalid algorithm. This results in a null value for all inputs. Try replacing 'SHA_512' in your SP with 'SHA2_512' and it will give you better results.

While this may be responsible for the rows not being inserted, you can't find out with the errors all disabled. TRY...CATCH is generally a bad idea when you're at this point in the code because it suppresses all those handy error messages that might otherwise tell you what went wrong.

I created a test table and uses your code (with the correct hashing algorithm name) and it worked fine for me. I assume that there is a constraint on your table - a unique index on the password field for instance - that is preventing the records from being added.

As a point of interest, you do know that random salt values make this completely unusable right? Even in example code it's better to have your salt represented by an obviously non-production value like 'stand-in the real salt value' or something.

Corey
  • 15,524
  • 2
  • 35
  • 68
  • In the cite you've posted is stated what I said, "stops message from **being return**", not what you said, that it stops to count – sepupic Nov 28 '19 at 11:36
  • @sepupic Read the whole quote "Stops the message *that shows the count of the number of rows affected*..." Note that I deleted my earlier comment and corrected my statement. – Corey Nov 28 '19 at 11:37
  • @Corey Removing this from the procedure still does not allow for me to enter data into the table and stays blank. Do you know how I would edit the code for it to allow me to enter data? Apologies if my question wasn't clear this has been stumping me for some time. – Displaza Nov 28 '19 at 11:41
  • @Corey Ok, now it sounds better. When I wroted my comment under the answer, your first comment still was there – sepupic Nov 28 '19 at 11:42
  • >>>TRY...CATCH is generally a bad idea when you're at this point in the code because it suppresses all those handy error messages<<< As I already said if you THROW the error within try..catch block it is passed to client. So try..catch is a very good idea, but you should use it correctly – sepupic Nov 28 '19 at 12:31
  • >>>that is preventing the records from being added<<< OP said the rows WERE inserted, the problem is that they are inserted with all NULLs – sepupic Nov 28 '19 at 12:37
0

Could the problem here be the @IsAdmin Parameter?

You're using the datatype SqlDbType.Bit (and the parameter is BIT also) which as far as I'm aware only accepts 0/1/NULL and you're setting the value to true/false. I get that sometimes true/false 0/1 are interchangeable but I'm not sure how C# would pass this and if it's a non issue but it's one thing to check.

Relevant question: DbType equivalent to SqlDbType.Bit

  • Apologies for not editing I managed to catch that out whilst waiting on responses to my question. Thank you for the input though. – Displaza Nov 28 '19 at 12:32