C# SQL, Multiple Commands - c#

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!

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();
}
}

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.

Related

No exception, but my data isn't written into the SQL Server database

I'm stuck at a loose end with inserting a new data entry into a SQL Server database. I have all the info I want to store in the following class:
public class NewSearchQuery //object reference q
{
public string Name, Location, SearchType, Path, Method;
public int RefNum;
public double Fee;
public bool Paid;
}
and after the user has filled in the form etc.. this is my code to save the info to the database:
bool complete;
string sql = $"Insert into PrivateLog (Id,Applicant,ApplicationDate,Location,Search,Paid,Method,Amount,Files) values({q.RefNum}, '{q.Name}', {AppDate}, '{q.Location}', '{q.SearchType}', {q.Paid}, '{q.Method}', {q.Fee}, '{q.Path}')";
cnn.Open();
try
{
SqlDataAdapter adapter = new SqlDataAdapter();
SqlCommand command = new SqlCommand(sql, cnn); //The Connection String cnn is in a public string variable above this method.
adapter.InsertCommand = new SqlCommand(sql, cnn);
command.Dispose();
complete = true;
}
catch (System.Exception e)
{
complete = false;
}
cnn.Close();
return complete;
Here is what my table designer looks like:
Can anyone show me why the new data entry might not be going through?
You don't need an SqlDataAdapter in that case, you can simply execute your command:
try
{
SqlCommand command = new SqlCommand(sql, cnn);
command.ExecuteNonQuery();
complete = true;
}
Although I recommend using command.Parameters to add your parameters values, protecting a possible SQL injection:
bool complete;
string sql = "Insert into PrivateLog (Id, Applicant, ApplicationDate, Location, Search, Paid, Method, Amount, Files) values(#RefNum, #Name, #AppDate, #Location, #SearchType, #Paid, #Method, #Fee, #Path)";
cnn.Open();
try
{
SqlCommand command = new SqlCommand(sql, cnn);
command.Parameters.Add("#RefNum", SqlDbType.Int).Value = q.RefNum;
command.Parameters.Add("#Name", SqlDbType.VarChar).Value = q.Name;
command.Parameters.Add("#AppDate", SqlDbType.DateTime).Value = AppDate;
command.Parameters.Add("#Location", SqlDbType.VarChar).Value = q.Location;
command.Parameters.Add("#SearchType", SqlDbType.VarChar).Value = q.SearchType;
command.Parameters.Add("#Paid", SqlDbType.Bit).Value = q.Paid;
command.Parameters.Add("#Method", SqlDbType.VarChar).Value = q.Method;
command.Parameters.Add("#Fee", SqlDbType.Decimal).Value = q.Fee;
command.Parameters.Add("#Path", SqlDbType.VarChar).Value = q.Path;
command.ExecuteNonQuery();
command.Dispose();
complete = true;
}
catch (System.Exception e)
{
complete = false;
}
cnn.Close();
return complete;

Procedure or function sp_Record has too many arguments specified

This is the code I'm using to insert records into SQL SERVER DB
while (dr.Read())
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "sp_InsertEmailRecord";
cmd.Connection = con;
cmd.Parameters.AddWithValue("#mId", memberId);
cmd.Parameters.AddWithValue("#companyName", dr.GetValue(0));
con.Open();
cmd.ExecuteNonQuery();
con.Close();
}
First Record insert successfully, but when it runs for second it return error
Procedure or function sp_Record has too many arguments specified
parameters added up
mean in first insert parameters = 2 & in next it added 2 more....
how to resolve???
Because your SqlCommand has already defined #mId and #companyName parameter since you didn't clear your parameters.
You have 2 options;
Use cmd.Parameters.Clear() to clear your parameter at the end of your while statement.
Or create a new SqlCommand object top of your while statement like SqlCommand cmd = new SqlCommand()
Looks like most correct way is to define your #companyName parameter name before your while and add it's value in while statement like John Saunders mentioned.
Also use using statement to dispose your SqlDataReader like;
using (SqlDataReader dr = cmd.ExecuteReader())
{
if (dr != null)
{
while (dr.Read())
{
//do something
}
}
}
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "sp_InsertEmailRecord";
cmd.Connection = con;
cmd.Parameters.AddWithValue("#mId", memberId);
cmd.Parameters.Add("#companyName");
con.Open();
while (dr.Read())
{
cmd.Parameters["#companyName"].Value = dr.GetValue(0);
cmd.ExecuteNonQuery();
}
con.Close();

Calling Functions in SqlCommand

public void CreateMySqlCommand()
{
SqlCommand myCommand = new SqlCommand();
myCommand.CommandText = "SELECT * FROM Categories ORDER BY CategoryID";
myCommand.CommandTimeout = 15;
myCommand.CommandType = CommandType.Text;
}
Can I use Sql Server functions in myCommand.CommandText and why?
If you mean, SQL Server user defined functions. Then, yes; you can use it normally like:
myCommand.CommandText = "SELECT fn_Yourfunctionname(#parameternames)";
myCommand.CommandType = CommandType.Text;
myCommand.Parameters.Add(new SqlParameter("#parameternames", ...
The reason it works is because this is the way that functions are called in SQL Server directly.
Another approach:
public T ExecuteScalarFunction<T>(string functionName, List<SqlParameter> parameters, SqlDbType returnSqlType) where T : new()
{
using (var conn = new SqlConnection(CTX.Database.GetDbConnection().ConnectionString))
{
if (conn.State != ConnectionState.Open)
conn.Open();
using (var cmd = new SqlCommand(functionName, conn)) {
cmd.CommandType = CommandType.StoredProcedure;
SqlCommandBuilder.DeriveParameters(cmd);
foreach (var parameter in parameters)
{
cmd.Parameters.Add(parameter);
}
cmd.Parameters.Add("#RETURN_VALUE", returnSqlType).Direction = ParameterDirection.ReturnValue;
cmd.ExecuteNonQuery();
return (T)cmd.Parameters["#RETURN_VALUE"].Value;
}
}
}

Reusing SqlCommand?

I am not really sure if this is possible or not.
I am currently working on a college project and I have a function that uses stored procedures. I would like to know if it is possible to take the same SqlCommand instance and apply updated parameters to call into the stored procedure again within the same function.
Lets say i have something like this in my code:
myConStr = ConfigurationManager.ConnectionStrings["MyConnString"].ConnectionString;
myConn = new SqlConnection(myConStr);
myCommand = new System.Data.SqlClient.SqlCommand("team5UserCurrentBooks3", myConn);
myCommand.CommandType = CommandType.StoredProcedure;
myCommand.Parameters.AddWithValue("#book_id", bookID);
myCommand.Parameters.AddWithValue("#user_id", userID);
try
{
myConn.Open();
myCommand.ExecuteNonQuery();
Is it possible to update MyCommand's parameters and call the stored procedure again?
Yes. You'll want to make sure that you call myCommand.Parameters.Clear between each call in order to dump the parameters, but there's nothing stopping you from reusing the object. (I don't use C# often, so this may have an error or two in the text)
myConStr = ConfigurationManager.ConnectionStrings["MyConnString"].ConnectionString;
myConn = new SqlConnection(myConStr);
myConn.Open();
myCommand = new System.Data.SqlClient.SqlCommand("team5UserCurrentBooks3", myConn);
myCommand.CommandType = CommandType.StoredProcedure;
myCommand.Parameters.AddWithValue("#book_id", bookID);
myCommand.Parameters.AddWithValue("#user_id", userID);
myCommand.ExecuteNonQuery();
myCommand.Parameters.Clear();
myCommand.CommandText= "NewStoredProcedureName";
myCommand.CommandType = CommandType.StoredProcedure;
myCommand.Parameters.AddWithValue("#foo_id", fooId);
myCommand.Parameters.AddWithValue("#bar_id", barId);
mycommand.ExecuteNonQuery();
myCommand.Parameters.Clear();
myCommand.CommandText = " SELECT * FROM table1 WHERE ID = #TID;"
myCommand.CommandType = CommandType.Text;
myCommand.Parameters.AddWithValue("#tid", tId);
SqlReader rdr;
rdr = myCommand.ExecuteReader();
Yes! You can definitely do that. Within a function you can re-use the same connection as well (I don't recommend re-using a connection object for larger scopes, but it is possible).
You could also do something like this:
myConStr = ConfigurationManager.ConnectionStrings["MyConnString"].ConnectionString;
using (var cn = new SqlConnection(myConStr) )
using (var cmd = new SqlCommand("team5UserCurrentBooks3", cn) )
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#user_id", SqlDbType.Int).Value = userID;
cmd.Parameters.Add("#book_id", SqlDbType.Int);
cn.Open();
for(int i = 0; i<10; i++)
{
cmd.Parameters["#book_id"].Value = i;
cmd.ExecuteNonQuery();
}
}
This will run the query 10 times and use the same user_id each time it executes, but change the book_id. The using block is just like wrapping your connection in a try/catch to make sure it's closed.

Passing sql statements as strings to mssql with C#?

This is a really, really stupid question but I am so accustomed to using linq / other methods for connecting and querying a database that I never stopped to learn how to do it from the ground up.
Question: How do I establish a manual connection to a database and pass it a string param in C#? (yes, I know.. pure ignorance).
Thanks
using (SqlConnection conn = new SqlConnection(databaseConnectionString))
{
using (SqlCommand cmd = conn.CreateCommand())
{
cmd.CommandText = "StoredProcedureName";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#ID", fileID);
conn.Open();
using (SqlDataReader rdr =
cmd.ExecuteReader(CommandBehavior.CloseConnection))
{
if (rdr.Read())
{
// process row from resultset;
}
}
}
}
One uses the SqlCommand class to execute commands (either stored procedures or sql) on SQL Server using ado.net. Tutorials abound.
Here's an example from http://www.csharp-station.com/Tutorials/AdoDotNet/Lesson07.aspx
public void RunStoredProcParams()
{
SqlConnection conn = null;
SqlDataReader rdr = null;
// typically obtained from user
// input, but we take a short cut
string custId = "FURIB";
Console.WriteLine("\nCustomer Order History:\n");
try
{
// create and open a connection object
conn = new
SqlConnection("Server=(local);DataBase=Northwind;Integrated Security=SSPI");
conn.Open();
// 1. create a command object identifying
// the stored procedure
SqlCommand cmd = new SqlCommand(
"CustOrderHist", conn);
// 2. set the command object so it knows
// to execute a stored procedure
cmd.CommandType = CommandType.StoredProcedure;
// 3. add parameter to command, which
// will be passed to the stored procedure
cmd.Parameters.Add(
new SqlParameter("#CustomerID", custId));
// execute the command
rdr = cmd.ExecuteReader();
// iterate through results, printing each to console
while (rdr.Read())
{
Console.WriteLine(
"Product: {0,-35} Total: {1,2}",
rdr["ProductName"],
rdr["Total"]);
}
}
finally
{
if (conn != null)
{
conn.Close();
}
if (rdr != null)
{
rdr.Close();
}
}
}
3 things no one else has shown you yet:
"Stacking" using statements
Setting an explicit parameter type rather than letting .Net try to pick one for you
"var" keyword
.
string sql = "MyProcedureName";
using (var cn = new SqlConnection(databaseConnectionString))
using (var cmd = new SqlCommand(sql, cn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#ParameterName", SqlDbType.VarChar, 50)
.Value = "MyParameterValue";
conn.Open();
using (SqlDataReader rdr =
cmd.ExecuteReader(CommandBehavior.CloseConnection))
{
if (rdr.Read())
{
// process row from resultset;
}
}
}

Categories