错误当使用2个不同的命令时,“已经有一个与此命令关联的打开的DataReader必须先closures”

我有这个遗留代码:

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

我已经创build了两个分离的命令来解决这个问题,但是它仍然继续:“已经有一个打开的DataReader与这个Command关联,必须先closures它”。

其他信息:相同的代码在另一个应用程序中工作。

我build议为第二个命令创build一个额外的连接,将解决它。 尝试在一个查询中结合两个查询。 为计数创build一个子查询。

 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 } 

就够了。

只需在连接string中添加以下内容:

 MultipleActiveResultSets=True; 
  1. 最佳的解决scheme可能是尝试将您的解决scheme转换为无需一次打开两个阅读器的表单。 理想情况下,它可能是一个单一的查询。 我现在没有时间这样做。
  2. 如果你的问题非常特殊,你需要让更多的阅读器同时打开,并且你的需求不能比SQL Server 2005 DB后端更早,那么魔术字就是MARS(多活动结果集)http://msdn.microsoft.com/en-us/library/ms345109%28v=SQL.90%29.aspx 。 Bob Vale的链接主题的解决scheme显示了如何启用它:在连接string中指定MultipleActiveResultSets=true 。 我只是说这是一个有趣的可能性,但你应该改变你的解决scheme。

    • 为了避免提到的SQL注入的可能性,请将参数设置为SQLCommand本身,而不是将它们embedded到查询string中。 查询string应该只包含您传递给SqlCommand的参数的引用。

我敢打赌,这个问题正在显示在这一行

 SqlDataReader dr3 = com2.ExecuteReader(); 

我build议你执行第一个阅读器,并做一个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; 

当你在同一个连接上有two different commands时,你可以得到这样的问题 – 尤其是在loop调用第二个命令。 这是从第一个命令返回的每个logging调用第二个命令。 如果第一个命令返回的logging有一万条,这个问题就更有可能了。

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

注意: MARS可能是一个解决scheme – 但它可能是有风险的,许多人不喜欢它。

参考

  1. 什么是“当前命令发生严重错误,结果,如果有的话,应该丢弃。” SQL Azure错误是什么意思?
  2. Linq-To-Sql和MARS的困境 – 当前命令发生严重错误。 如果有的话,结果应该被丢弃
  3. 在DataTable上复杂的GROUP BY

尝试组合查询,它将比每行执行一个额外的查询运行得更快。 我不喜欢你使用的string,我会创build一个类来保存信息。

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

未经testing,但也许给出一些想法。

MultipleActiveResultSets=true添加到连接string的提供者部分。 看下面的例子:

 <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" />