Using C# to loop through and insert data - c#

I have a program that has to loop through a Scenarios database, and for each while loop iteration, update a second Results database. Here's the segment of code.
public void TestScenarios(SqlConnection connection)
{
using (SqlCommand cmd = new SqlCommand("SELECT * FROM Scenarios", connection))
{
SqlDataReader reader = cmd.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
string Id = reader["ScenarioID"].ToString();
string Data = reader["ScenarioData"].ToString();
string Url = reader["ScenarioURL"].ToString();
webBrowser1.Navigate(Url);
InsertResults(connection, Id);
}
}
reader.Close();
}
}
public void InsertResults(SqlConnection conn, string Id)
{
using (SqlCommand cmd = new SqlCommand("INSERT INTO Results VALUES(" +
"#ResultID, #HasSucceeded, #ScenarioID, #Screenshot)", conn))
{
cmd.Parameters.AddWithValue("#ResultID", 0);
cmd.Parameters.AddWithValue("#HasSucceeded", 0);
cmd.Parameters.AddWithValue("#ScenarioID", Id);
cmd.Parameters.AddWithValue("#Screenshot", "screenshot.jpeg");
}
}
It's not working, and I'm sure I did tons of things wrong, but I'm having trouble finding direction.

Your second command is never executing. Add:
cmd.ExecuteNonQuery();

May I propose you to use SqlBulkCopy? Here is some information on how to copy two database tables , I hope you'll find it clear.
http://www.codeproject.com/Articles/18418/Transferring-Data-Using-SqlBulkCopy
http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlbulkcopy%28v=vs.90%29.aspx

Related

Complicated Report Requires SQL Temp File C# Returns No Rows

Here is the problem. I have to write a long, hella complicated report. I don't think I can do it using only C# so I thought the best thing was a temporary SQL table. So I wrote this code and I always get no rows and I know there is data in the table.
SqlConnection connection = new SqlConnection(connectionString);
SqlCommand cmd = new SqlCommand();
SqlDataReader reader;
cmd.CommandText = "SELECT * INTO ##temp FROM Customers";
cmd.Connection = connection;
connection.Open();
reader = cmd.ExecuteReader();
if (reader.HasRows)
{ MessageBox.Show("ROWS"); }
else
{ MessageBox.Show("NO ROWS"); }
connection.Close();
So I thought, maybe I need another reader to SELECT * FROM ##TEMP but it always crashes telling me that my reader is already open (I used reader = cmd.ExecuteReader();). Please help.
I found the answer:
cmd.Connection = connection;
connection.Open();
cmd.ExecuteNonQuery();
cmd.CommandText = "SELECT * from ##temp";
reader = cmd.ExecuteReader();
First run ExecuteNonQuery, then change the command then run ExecuteReader
Your question fails to justify the use of a temporary table. There really is no reason for you not to simply read the data directly from the actual table (unless your question omitted important and relevant details).
Also, it's a good idea to get in the habit of disposing the db objects with using blocks to clean up properly even in the event of exceptions.
Here is what the code could look like:
using (var connection = new SqlConnection(connectionString))
{
using (var cmd = new SqlCommand("SELECT * FROM Customers", connection))
{
connection.Open();
using (var reader = cmd.ExecuteReader())
{
if (reader.HasRows)
{
MessageBox.Show("ROWS");
}
else
{
MessageBox.Show("NO ROWS");
}
}
}
}

C# SQL, Multiple Commands

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.

Stored procedure has no parameters and arguments were supplied

I have a function which will get the records from the database.
public List<Issue> Load_Issues()
{
SqlDataReader Sdr;
List<Issue> ObjList = new List<Issue>();
cmd.CommandText = "Get_All_Issue";
try
{
cmd.Connection = con;
cmd.CommandType = CommandType.StoredProcedure;
con.Open();
Sdr = cmd.ExecuteReader();
while (Sdr.Read())
{
// here I pull out records from database..
}
}
catch (Exception ex)
{
throw ex;
}
finally
{
con.Close();
}
return ObjList;
}
The function I am using to bind the Gridview is as follows
public void Bind_Issues()
{
gdIssues.DataSource = Bl.Load_Issues()();
gdIssues.DataBind();
}
My stored procedure doesn't take any arguments. While the page loads for the first time it is working fine and binding the records to the gridview.
We have option to edit the records also, so what happening is after updating records I need to again bind the records to gridview. So I am again using my Load_Issues function to do it. But this time it is throwing error
Get_All_Issues has no parameters and arguments were supplied
You're most probably re-using the cmd instance in multiple places and you don't clear the parameters associated with it, thus creating the exception you're seeing.
Easiest fix would be to not re-use cmd, but if for whatever reason it's better for you, just make sure you use Clear on parameters before you execute it.
cmd.Parameters.Clear();
Try not using global connections, commands etc: open and close them within the method
public List<Issue> Load_Issues() {
//TODO: Put actual connection string here
using (SqlConnection con = new SqlConnection("Connection String here")) {
con.Open();
// Put IDisposable into using
using (SqlCommand cmd = new SqlCommand()) {
cmd.Connection = con;
cmd.CommandText = "Get_All_Issue";
cmd.CommandType = CommandType.StoredProcedure;
List<Issue> ObjList = new List<Issue>();
// Put IDisposable into using
using (var Sdr = cmd.ExecuteReader()) {
while (Sdr.Read()) {
//TODO: Pull out records from database into ObjList
}
}
return ObjList;
}
}
}
Try these
exec 'stored_procedure_name'
go
or
alter proc stored_procedure_name
as
begin
--Block of Statements
end
go
or
create proc stored_procedure_name
as
begin
--Block of Statements
end
go
Where go keyword will solved your problem.

C# - Increase method reusability

private static void ReadOrderData(string connectionString)
{
string queryString =
"SELECT OrderID, CustomerID FROM dbo.Orders;";
using (SqlConnection connection =
new SqlConnection(connectionString))
{
SqlCommand command =
new SqlCommand(queryString, connection);
connection.Open();
SqlDataReader reader = command.ExecuteReader();
// Call Read before accessing data.
while (reader.Read())
{
Console.WriteLine(String.Format("{0}, {1}",
reader[0], reader[1]));
}
// Call Close when done reading.
reader.Close();
}
}
How can I enhance the method above to accept any queryString? The problem is in the while. There's a fixed # of columns I can read. I want to be able to read any number of columns so that I can populate and return a DataSet. How can I do it?
You can do something along these lines:
private static void ReadOrderData(string connectionString,
string query, Action<SqlDataReader> action)
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
SqlCommand command =
new SqlCommand(query, connection);
connection.Open();
SqlDataReader reader = command.ExecuteReader();
// Call Read before accessing data.
while (reader.Read())
{
action(reader);
}
// Call Close when done reading.
reader.Close();
}
}
You're really barking up the wrong tree. You shouldn't be using "methods that accept query strings". You should raise the level of abstraction by using Entity Framework or the like.
You will then not need to use the above code because it will not exist. Those who would have called that code will do something like this:
var orders = from o in ordersDAL.Orders
select new {o.OrderID, o.CustomerID};
foreach (var order in orders)
{
Console.WriteLine("{0}, {1}", order.OrderID, order.CustomerID);
}
Your code is badly designed in any case. Why in the world would you combine the fetching of the data with the use of it? That while loop should not be in that same method.
I would use something like the answer from obrok, but I would add the ability to use parameters.
Also, the SqlCommand and SqlDataReader both need to be within a using block:
private static void ReadOrderData(string connectionString,
string query, Action<SqlDataReader> action)
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
using (SqlCommand command =
new SqlCommand(query, connection)) {
connection.Open();
using (SqlDataReader reader = command.ExecuteReader()) {
// Call Read before accessing data.
while (reader.Read())
{
action(reader);
}
// No need to call Close when done reading.
// reader.Close();
} // End SqlDataReader
} // End SqlCommand
}
}
Use SqlDataAdapter:
private static DataSet ReadData(string connectionString, string queryString)
{
DataSet dataSet;
using (SqlConnection connection =
new SqlConnection(connectionString))
{
connection.Open();
SqlCommand command =
new SqlCommand(queryString, connection);
SqlDataAdapter adapter = new SqlDataAdapter(command);
adapter.Fill(dataSet);
}
return dataSet;
}
Or something like this.
How I'm doing this:
(basically idea is to use DataSet along with IDataAdapter)
DataSet ds = null;
List<SqlParameter> spParams = ...
using (SqlConnection connection = new SqlConnection(ConnectionString))
{
using (SqlCommand command = new SqlCommand(spName, connection))
{
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Clear();
command.Parameters.AddRange(spParams);
connection.Open();
IDbDataAdapter da = new SqlDataAdapter();
da.SelectCommand = command;
ds = new DataSet("rawData");
da.Fill(ds);
ds.Tables[0].TableName = "row";
foreach (DataColumn c in ds.Tables[0].Columns)
{
c.ColumnMapping = MappingType.Attribute;
}
}
}
// here is you have DataSet flled in by data so could delegate processing up to the particular DAL client
You said you tried EF, but did you try Dapper ? looks like a simple enough ORM that should work with your database (it's raw SQL), and will avoid you most of this mapping code. Dapper is used in StackOverflow so it cannot be too bad :)
Same suggestion as 'WorkerThread' but change the Method Signature to:
private static DataSet ReadOrderData(string connectionString, string queryString)
{
// do work
}
Drop the following line from 'WorkerThread' example:
string queryString = "SELECT OrderID, CustomerID FROM dbo.Orders;";
Once you have made these two changes to 'WorkerThread's' method it should be perfect for what you need.
Look at the DataTable.Load method. Or, for a finer level of control, check out the properties and methods on IDataReader, such as FieldCount, GetFieldType, and GetName.

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