【问题标题】:Getting "Invalid attempt to call Read when reader is closed"获取“阅读器关闭时尝试调用读取无效”
【发布时间】:2012-11-19 14:36:11
【问题描述】:

我有以下代码(为了便于阅读,这里使用伪值),其中第一个连接返回大量数据(数千行)。 SqlDataReader 通过reader.Read() 将它们一一读取,然后打开一个新连接以更新每一行的新值:

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
                }
            }
        }
    }

但它会引发错误:

[InvalidOperationException: Invalid attempt to call Read when reader is closed.]
System.Data.SqlClient.SqlDataReader.ReadInternal(Boolean setTimeout) +640
System.Data.SqlClient.SqlDataReader.Read() +9

在开发环境中它工作正常,但这里只有几百行。它立即抛出错误,所以它看起来不像是某种超时,但是嘿 - 我不知道......

【问题讨论】:

  • 调用 Read on reader 时。 while (reader.Read())
  • 你能在你的第一个 SPROC 中用 SQL 做这个吗?
  • 这段代码看起来不错,一定有你遗漏的东西。执行它的用户是否可能没有权限或类似的东西?
  • 在 SQL 中执行此操作?临时不,因为散列是用一些库动态创建的......
  • 用户拥有正确的权限。在同一页面上,我使用相同的连接字符串和相同的设置做一些其他的事情。用户是拥有所有 SP 权限的 SA

标签: c# sql


【解决方案1】:

不知道为什么会这样,但在迭代同一个数据库的实时连接时执行查询确实是个坏主意。请记住,只要您使用 DataReader 迭代记录,连接就处于活动状态。

更糟糕的是打开然后关闭一个连接快速连续数千次。仅此一项就可以使任何数据库陷入瘫痪。

改变你的逻辑,将你需要的值存储在一个局部变量中(结构无关紧要),然后只使用一个连接来执行你需要的所有存储过程。

例如:

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

一个连接,一个命令执行所有存储过程。真的不需要更多。

【讨论】:

  • 如果您使用连接池,“打开然后关闭数千个连接”非常便宜。
  • 谢谢。做了一点重写,但几乎遵循了这个 - 它有效......虽然仍然不知道错误,但希望我不会再看到它!
  • 干杯 @Thomas 数据库上的过载可能导致一些内部超时,结果 DataReader 被关闭。现在您没有创建任何额外的过载,所以它不会发生。 :)
猜你喜欢
  • 2011-08-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-29
相关资源
最近更新 更多