使用2个不同的命令时出现错误“此命令已关联了一个打开的DataReader,必须先关闭它”


69

我有这个旧代码:

 private void conecta()
 {  
     if (conexao.State == ConnectionState.Closed)
         conexao.Open();
 }

 public List<string[]> get_dados_historico_verificacao_email_WEB(string email)
 {
     List<string[]> historicos = new List<string[]>();
     conecta();

     sql = 
         @"SELECT * 
         FROM historico_verificacao_email 
         WHERE nm_email = '" + email + @"' 
         ORDER BY dt_verificacao_email DESC, hr_verificacao_email DESC";

     com = new SqlCommand(sql, conexao);
     SqlDataReader dr = com.ExecuteReader();

     if (dr.HasRows)
     {
         while (dr.Read())
         {
             string[] dados_historico = new string[6];
             dados_historico[0] = dr["nm_email"].ToString();
             dados_historico[1] = dr["dt_verificacao_email"].ToString();
             dados_historico[1] = dados_historico[1].Substring(0, 10);
             dados_historico[2] = dr["hr_verificacao_email"].ToString();
             dados_historico[3] = dr["ds_tipo_verificacao"].ToString();

             sql = 
                 @"SELECT COUNT(e.cd_historico_verificacao_email) QT 
                 FROM emails_lidos e 
                 WHERE e.cd_historico_verificacao_email = 
                     '" + dr["cd_historico_verificacao_email"].ToString() + "'";

             tipo_sql = "seleção";
             conecta();
             com2 = new SqlCommand(sql, conexao);

             SqlDataReader dr3 = com2.ExecuteReader();
             while (dr3.Read())
             {
                 //quantidade de emails lidos naquela verificação
                 dados_historico[4] = dr3["QT"].ToString(); 
             }
             dr3.Close();
             conexao.Close();

             //login
             dados_historico[5] = dr["cd_login_usuario"].ToString();
             historicos.Add(dados_historico);
         }
         dr.Close();
     }
     else
     { 
         dr.Close();
     }

     conexao.Close();
     return historicos;
 }


我创建了两个单独的命令来更正此问题,但该问题仍在继续:“已经存在与此命令相关联的打开的DataReader,必须首先关闭它”。

附加信息:相同的代码正在另一个应用程序中运行。


30
您有一个SQL注入漏洞。
SLaks


4
这不仅仅是通过命令,而是通过连接。如果两个命令都使用相同的连接,则会收到错误消息。
Michael Todd

为什么不将内部命令放入附加功能中?它增强了可读性。
sockfd

Answers:


8

我建议为第二个命令创建一个附加连接,即可解决该问题。尝试将两个查询合并到一个查询中。创建一个子查询的计数。

while (dr3.Read())
{
    dados_historico[4] = dr3["QT"].ToString(); //quantidade de emails lidos naquela verificação
}

为什么要一次又一次覆盖相同的值?

if (dr3.Read())
{
    dados_historico[4] = dr3["QT"].ToString(); //quantidade de emails lidos naquela verificação
}

足够了。


8
创建与同一数据库的第二个连接也许可以解决问题,但无助于代码的可读性和维护性
Mauricio Gracia Gutierrez 2013年

+1。这样,他甚至可以独立于服务器供应商,尽管为此,他必须使用DbConnection而不是SqlConnection,并且Sql *类的Db *类isnetad。但是最好的办法是,就像他(和任何答案)所说的那样,他重写自己的查询。
Csaba Toth 2013年


21
  1. 最佳解决方案是尝试将您的解决方案转换为无需一次打开两个阅读器的形式。理想情况下,它可以是单个查询。我现在没有时间这样做。
  2. 如果您的问题如此特殊,以至于您确实确实需要同时打开更多阅读器,并且您的要求允许使用不早于SQL Server 2005 DB后端,那么魔术词就是MARS(多个活动结果集)http://msdn.microsoft.com/zh-cn/library/ms345109%28v=SQL.90%29.aspx。Bob Vale的链接主题的解决方案显示了如何启用它:MultipleActiveResultSets=true在您的连接字符串中指定。我只是说这是一种有趣的可能性,但是您应该改变解决方案。

    • 为了避免上述提到的SQL注入可能性,请将参数设置为SQLCommand本身,而不是将其嵌入查询字符串中。查询字符串应仅包含对传递给SqlCommand的参数的引用。

Csaba Toth MARS设置可以在不是MS SQL SERVER的另一个数据库上工作吗?
Mauricio Gracia Gutierrez

这是MS SQL Server功能。2005年及以后。其他RDBMS也可能具有类似的功能,但特定于特定的服务器类型。
卡萨巴·托特

1
可能有不利的方面:stackoverflow.com/questions/374444/...
乔鲍托特

1
开始着手进行单个查询。您是否尝试过一些JOIN或嵌入式查询?
卡萨巴·托特

13

当您two different commands使用相同的连接时,可能会遇到这样的问题-特别是在.NET中调用第二个命令时loop。那就是为从第一个命令返回的每个记录调用第二个命令。如果第一个命令返回了大约10,000条记录,则更可能发生此问题。

我以前通过将其作为单个命令来避免这种情况。第一个命令返回所有必需的数据并将其加载到DataTable中。

注意:这MARS可能是一个解决方案-但可能会有风险,许多人不喜欢它。

参考

  1. “当前命令发生严重错误。如果有结果,应将其丢弃。” SQL Azure错误是什么意思?
  2. Linq-To-Sql和MARS出现问题-当前命令发生了严重错误。结果(如有)应丢弃
  3. 数据表上的复杂GROUP BY

2
关键:如果在IQueryable上调用foreach()(您执行了.Where(),但未在其后添加.ToList()),然后尝试在该循环中使用上下文,则可能会导致问题。
Don Cheadle

1
谢谢@DonCheadle,这正是我遇到的问题。ToList()解决了这个问题!谢谢!
mack

8

我敢打赌这一行会显示问题

SqlDataReader dr3 = com2.ExecuteReader();

我建议你执行的第一个读者,做一个dr.Close();和迭代historicos,用另一个循环,执行com2.ExecuteReader()

public List<string[]> get_dados_historico_verificacao_email_WEB(string email)
    {

        List<string[]> historicos = new List<string[]>();
        conecta();
        sql = "SELECT * FROM historico_verificacao_email WHERE nm_email = '" + email + "' ORDER BY  dt_verificacao_email DESC, hr_verificacao_email DESC"; 
        com = new SqlCommand(sql, conexao);
        SqlDataReader dr = com.ExecuteReader();

        if (dr.HasRows)
        {
            while (dr.Read())
            {
                string[] dados_historico = new string[6];
                dados_historico[0] = dr["nm_email"].ToString();
                dados_historico[1] = dr["dt_verificacao_email"].ToString();
                dados_historico[1] = dados_historico[1].Substring(0, 10);
                //System.Windows.Forms.MessageBox.Show(dados_historico[1]);
                dados_historico[2] = dr["hr_verificacao_email"].ToString();
                dados_historico[3] = dr["ds_tipo_verificacao"].ToString();
                dados_historico[5] = dr["cd_login_usuario"].ToString();
                historicos.Add(dados_historico);
            }

            dr.Close();

            sql = "SELECT COUNT(e.cd_historico_verificacao_email) QT FROM emails_lidos e WHERE e.cd_historico_verificacao_email = '" + dr["cd_historico_verificacao_email"].ToString() + "'";
            tipo_sql = "seleção";
            com2 = new SqlCommand(sql, conexao);

            for(int i = 0 ; i < historicos.Count() ; i++)
            {
                SqlDataReader dr3 = com2.ExecuteReader();
                while (dr3.Read())
                {
                    historicos[i][4] = dr3["QT"].ToString(); //quantidade de emails lidos naquela verificação
                }
                dr3.Close();
            }

        }

        return historicos;

@CsabaToth两个循环,一个循环将数据读入dados_historico并关闭读取器,然后另一个循环执行另一个读取器
Mauricio Gracia Gutierrez

我知道+1。貌似他将数据收集到某个数组中,以便他可以重用它。除非有更好的单个查询,否则您的解决方案是真正的解决方案。
Csaba Toth 2013年

3

尝试合并查询,它将比每行执行一个附加查询快得多。我不喜欢您使用的string [],我会创建一个用于保存信息的类。

    public List<string[]> get_dados_historico_verificacao_email_WEB(string email)
    {
        List<string[]> historicos = new List<string[]>();

        using (SqlConnection conexao = new SqlConnection("ConnectionString"))
        {
            string sql =
                @"SELECT    *, 
                            (   SELECT      COUNT(e.cd_historico_verificacao_email) 
                                FROM        emails_lidos e 
                                WHERE       e.cd_historico_verificacao_email = a.nm_email ) QT
                  FROM      historico_verificacao_email a
                  WHERE     nm_email = @email
                  ORDER BY  dt_verificacao_email DESC, 
                            hr_verificacao_email DESC";

            using (SqlCommand com = new SqlCommand(sql, conexao))
            {
                com.Parameters.Add("email", SqlDbType.VarChar).Value = email;

                SqlDataReader dr = com.ExecuteReader();

                while (dr.Read())
                {
                    string[] dados_historico = new string[6];
                    dados_historico[0] = dr["nm_email"].ToString();
                    dados_historico[1] = dr["dt_verificacao_email"].ToString();
                    dados_historico[1] = dados_historico[1].Substring(0, 10);
                    //System.Windows.Forms.MessageBox.Show(dados_historico[1]);
                    dados_historico[2] = dr["hr_verificacao_email"].ToString();
                    dados_historico[3] = dr["ds_tipo_verificacao"].ToString();
                    dados_historico[4] = dr["QT"].ToString();
                    dados_historico[5] = dr["cd_login_usuario"].ToString();

                    historicos.Add(dados_historico);
                }
            }
        }
        return historicos;
    }

未经测试,但也许e给出了一些想法。


2

MultipleActiveResultSets=true连接字符串添加到提供程序部分。请参阅以下示例:

<add name="DbContext" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=dbName;Persist Security Info=True;User ID=userName;Password=password;MultipleActiveResultSets=True" providerName="System.Data.SqlClient" />
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.