Thomas
Thomas

Reputation: 1563

Getting "Invalid attempt to call Read when reader is closed"

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

Answers (1)

Shadow Wizard
Shadow Wizard

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

Related Questions