Reputation: 1563
I've got the following code (here with pseudovalues for readability), where the first connection returns a lot of data (thousands of rows). SqlDataReader reads them one by one by the reader.Read()
and then opens a new connection to update each row with new values:
using (SqlConnection conn = new SqlConnection(connString))
using (SqlCommand cmd = new SqlCommand("sp1", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("@param1", param1);
cmd.Connection.Open();
using (SqlDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
try
{
string hash= utils.SHA256.Hashing((string)reader["firstRow"], saltValue);
using (SqlConnection conn2 = new SqlConnection(connString))
using (SqlCommand cmd2 = new SqlCommand("sp2", conn2))
{
cmd2.CommandType = CommandType.StoredProcedure;
cmd2.Parameters.AddWithValue("@param1", param1);
cmd2.Parameters.AddWithValue("@param2", param2);
cmd2.Connection.Open();
cmd2.ExecuteNonQuery();
}
}
catch (SqlException ex)
{
//something
}
}
}
}
but it throws an error:
[InvalidOperationException: Invalid attempt to call Read when reader is closed.]
System.Data.SqlClient.SqlDataReader.ReadInternal(Boolean setTimeout) +640
System.Data.SqlClient.SqlDataReader.Read() +9
In development environment it works fine, but here there's only a few hundred rows. It throws the error immediately, so it doesn't directly look like some kind of timeout, but hey - I don't know...
Upvotes: 1
Views: 13430
Reputation: 66389
Don't know why it happens, but it's really a bad idea to execute queries while iterating a live connection to the same database. Keep in mind that as long as you iterate records with a DataReader, the connection is alive.
Much worse is opening then closing a connection thousands of times in a quick succession. This alone can bring any database down to its knees.
Change your logic, store the values you need in a local variable (structure doesn't matter) then use one connection only to execute all the stored procedures you need.
For example:
using (SqlConnection conn = new SqlConnection(connString))
{
conn.Open();
List<string[]> values = new List<string[]>();
using (SqlCommand cmd = new SqlCommand("sp1", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("@param1", param1);
using (SqlDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
try
{
string hash= utils.SHA256.Hashing((string)reader["firstRow"], saltValue);
string anotherValue = (string)reader["secondRow"];
values.Add(new string[] { hash, anotherValue });
}
catch (SqlException ex)
{
//something
}
}
reader.Close();
}
}
if (values.Count > 0)
{
using (SqlCommand cmd2 = new SqlCommand("sp2", conn))
{
cmd2.CommandType = CommandType.StoredProcedure;
cmd2.Parameters.AddWithValue("@param1", null);
cmd2.Parameters.AddWithValue("@param2", null);
values.ForEach(items =>
{
cmd2.Parameters["@param1"].Value = items[0];
cmd2.Parameters["@param2"].Value = items[1];
cmd2.ExecuteNonQuery();
});
}
}
conn.Close();
}
One connection, one command to execute all stored procedures. Really don't need more than that.
Upvotes: 5