3

I'm trying to write a method which should communicate with database, but I'm not sure if my approach is right.

public void dbWorkerLogin(int workerNumber) { 
    // Connection string stored in "conn"
    if (!new SqlCommand("Some Command WHERE id=" +workernumber,conn).executeReader().HasRows)
    {
       new SqlCommand("exec STORED_PROCEDURE1 " + workerNumber, conn).ExecuteNonQuery();
       new SqlCommand("exec STORED_PROCEDURE2 " + workerNumber, conn).ExecuteNonQuery();
    }
    else
    {
       new SqlCommand("exec STORED_PROCEDURE3 " + workerNumber,conn).ExecuteNonQuerry();
    }

1) Is it ok to write it like this and start each SqlCommand with keyword new? Or should I do something like:

SqlCommand command = new SqlCommand(null, conn);
command = ...;

and then recycle the variable 'command' or this way?

using(SqlCommand cmd = new SqlCommand("COMMAND", conn);

2) Will my procedures work or should I use SqlCommand.Prepare() function that will covert my data into correct datatypes? eg. workerNumber is int, but in database it is stored as decimal.

using (SqlCommand cmd = new SqlCommand("STORED_PROCEDURE", conn))
{
   cmd.CommandType = CommandType.StoredProcedure;
   cmd.Parametres.Add("@id", SqlDbType.Decimal).Value = workNumber;
   cmd.Prepare();
   cmd.ExecuteNonQuery();
}

Can you please somehow sum up what to use, what better not to? Unfortunately I can't test that first code because of limited access to DB so I'm not sure if it can be executed without errors or not. Thank you for any help on this subject!

EDIT: After a few hours I reach to this stage:

public int getWorkerNumber(string uniqueID)
 {
    using (conn = new SqlConnection(ConfigurationManager.ConnectionStrings["dbConnect"].ConnectionString))
    {
        conn.Open();
        using (SqlCommand cmd = new SqlCommand("SELECT number FROM worker WHERE workerID = @id",conn))
         {
            cmd.Parameters.Add("@id", SqlDbType.Decimal).Value = uniqueID;
            using (SqlDataReader reader = cmd.ExecuteReader())
            {
                int answer;
                while (reader.Read())
                {
                   answer = (int)reader.GetDecimal(0);
                }
                return answer;
            }
        }
    }
}

And this one:

public string dbLoginWorker(int workerNumber)
    {
        SqlCommand cmd;
        SqlDataReader reader;
        using (conn = new SqlConnection(ConfigurationManager.ConnectionStrings["dbConnect"].ConnectionString))
        {
            conn.Open();
            cmd = new SqlCommand("SELECT column FROM table WHERE id= @workernumber", conn);
            cmd.Parameters.Add("@workernumber", SqlDbType.Decimal).Value = workerNumber;
            reader = cmd.ExecuteReader();

            if (!reader.HasRows)
            {
                cmd = new SqlCommand("STORED_PROCEDURE1", conn);
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.Parameters.Add("@ID", SqlDbType.Decimal).Value = workerNumber;
                cmd.Parameters.Add("@VARCHAR", SqlDbType.VarChar).Value = "text";
                cmd.Prepare();
                reader.Close();
                cmd.ExecuteNonQuery();

                cmd.Dispose();
                reader.Dispose();
                return "procedure 1 executed";
           else
            {
                cmd = new SqlCommand("STORED_PROCEDURE2", conn);
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.Parameters.Add("@ID", SqlDbType.Decimal).Value = workerNumber;
                cmd.Parameters.Add("@INT", SqlDbType.SmallInt).Value = 1;
                cmd.Parameters.Add("@VARCHAR", SqlDbType.VarChar).Value = "text";
                cmd.Prepare();
                reader.Close();
                cmd.ExecuteNonQuery();

                cmd.Dispose();
                reader.Dispose();
                return "procedure 2 executed";
            }
        }
    }

Both methods are functional (if I did no mistake in rewriting :) ). I'm not sure which of these methods (1st or 2nd) are better in terms of stability and if this approach is better and more ressistant to SQL Injection. Can someone comment on this subject? Thank you again for any help!

Aldoras
  • 111
  • 7
  • just give a try and post errors – Tharif Aug 10 '15 at 10:37
  • `dbWorkerLogin` is vulnerable to [SQL Injection](https://www.owasp.org/index.php/SQL_Injection) as you allow unsanitised input here `"Some Command WHERE id=" +workernumber"`. – DGibbs Aug 10 '15 at 10:48

2 Answers2

2

1) It is best to always use USING blocks when possible. This includes SqlConnection, SqlCommand, SqlReader and other objects that implement IDisposable. USING blocks automatically close and dispose of the objects, so you do not have to do so.

2) I believe that you are using the Prepare() method in the wrong place. Look at the following StackOverflow article for proper usage: PrepareMethodInstructions.

3) in the dbLoginWorker() method, the first query is just used to determine if rows are found. Therefore, I suggest changing the SELECT command to SELECT TOP 1 column FROM table WHERE id= @workernumber so that the query is faster and more efficient.

4) I do not believe your commands are subject to SQL Injection attacks because they are fully parameterized. Good job on that one.

5) As a general thought, I suggest reading up on refactoring techniques. Your dbLoginWorker() method could be made more readable and maintainable, as well as self-documenting, if you created three additional methods, one for each SQL command, and named them something appropriate. You could also setup a method for creating a connection based on a connection name, and you would not have as much duplicate code. For example:

public static SqlConnection GetConnection(string connectionName)
{
    SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings[connectionName].ConnectionString);
    conn.Open();
    return conn;
}

public string dbLoginWorker(int workerNumber)
{
    using (conn = GetConnection("dbConnect"))
    {
        if (CanFindWorkerNumber(conn, workerNumber))
            ExecuteProcedure1(conn);
        else
            ExecuteProcedure2(conn);
    }
}

public bool CanFindWorkerNumber (SqlConnection conn, int workerNumber)
{
    bool success = false;
    using (SqlCommand cmd = new SqlCommand("SELECT TOP 1 column FROM table WHERE id= @workernumber", conn))
    {
        cmd.Parameters.Add("@workernumber", SqlDbType.Decimal);
        cmd.Prepare();
        cmd.Parameters[0].Value = workerNumber;
        success = cmd.ExecuteScalar() != null;      
    }
    return success;
}

public void ExecuteProcedure1(SqlConnection conn)
{
    using (SqlCommand cmd = new SqlCommand("STORED_PROCEDURE1", conn))
    {
        cmd.CommandType = CommandType.StoredProcedure;
        cmd.Parameters.Add("@ID", SqlDbType.Decimal);
        cmd.Parameters.Add("@VARCHAR", SqlDbType.VarChar);
        cmd.Prepare();
        cmd.Parameters[0].Value = workerNumber;
        cmd.Parameters[1].Value = "text";
        cmd.ExecuteNonQuery();
    }
}

public void ExecuteProcedure1(SqlConnection conn)
{
    using (SqlCommand cmd = new SqlCommand("STORED_PROCEDURE1", conn))
    {
        cmd.CommandType = CommandType.StoredProcedure;
        cmd.Parameters.Add("@ID", SqlDbType.Decimal);
        cmd.Parameters.Add("@INT", SqlDbType.SmallInt).Value);
        cmd.Parameters.Add("@VARCHAR", SqlDbType.VarChar);
        cmd.Prepare();
        cmd.Parameters[0] = workerNumber;
        cmd.Parameters[1] = 1;
        cmd.Parameters[2] = "text";
        cmd.ExecuteNonQuery();
    }
}
Community
  • 1
  • 1
Brian Payne
  • 424
  • 3
  • 6
1

You could actually do this in one SQL commend. Right now you are pulling back a result set only to see if it has rows or not, then executing different commands based on that. You should be able to do that in one command, disposing of it and the connection appropriately:

var sql = 
    @"
        IF EXISTS(Some Command WHERE id=@workernumber)
        BEGIN
            exec STORED_PROCEDURE1 @workernumber;    
            exec STORED_PROCEDURE2 @workernumber;
        END
        ELSE
            exec STORED_PROCEDURE3 @workernumber;
    ";

Note that you're not vulnerable to SQL injection because you're not dealing with strings, only integers.

D Stanley
  • 149,601
  • 11
  • 178
  • 240