c#

c# - Error "Ya hay un DataReader abierto asociado con este comando que debe cerrarse primero" cuando se usan 2 comandos distintos



(7)

  1. La solución óptima podría ser tratar de transformar su solución en un formato en el que no necesite tener dos lectores abiertos a la vez. Idealmente podría ser una sola consulta. No tengo tiempo para hacer eso ahora.
  2. Si su problema es tan especial que realmente necesita tener más lectores abiertos simultáneamente, y sus requisitos no son más antiguos que el servidor de SQL Server 2005 DB, entonces la palabra mágica es MARS (conjuntos de resultados activos múltiples) . http://msdn.microsoft.com/en-us/library/ms345109%28v=SQL.90%29.aspx . La solución del tema vinculado de Bob Vale muestra cómo habilitarla: especifique MultipleActiveResultSets=true en su cadena de conexión. Solo digo esto como una posibilidad interesante, pero deberías transformar tu solución.

    • para evitar la posibilidad de inyección SQL mencionada, establezca los parámetros en el propio SQLCommand en lugar de incrustarlos en la cadena de consulta. La cadena de consulta solo debe contener las referencias a los parámetros que pasa a SqlCommand.

Tengo este código heredado:

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


He creado dos comandos separados para corregir el problema, pero aún continúa: "Ya hay un DataReader abierto asociado con este comando que debe cerrarse primero".

Una información adicional: el mismo código funciona en otra aplicación.


Agregue MultipleActiveResultSets=true a la parte del proveedor de su cadena de conexión. Vea el ejemplo a continuación:

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


Apuesto a que el problema está siendo mostrado en esta línea.

SqlDataReader dr3 = com2.ExecuteReader();

Le sugiero que ejecute el primer lector y haga un dr.Close(); y los historicos iterantes, con otro bucle, ejecutando 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;


Intente combinar la consulta, se ejecutará mucho más rápido que ejecutar una consulta adicional por fila. Si no me gusta la cadena [] que está utilizando, crearía una clase para almacenar la información.

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

Sin probar, pero Maybee da una idea.


Puede obtener este problema cuando tiene two different commands en la misma conexión, especialmente al llamar al segundo comando en un loop . Eso es llamar al segundo comando para cada registro devuelto desde el primer comando. Si hay unos 10,000 registros devueltos por el primer comando, este problema será más probable.

Solía ​​evitar este escenario haciéndolo como un solo comando. El primer comando devuelve todos los datos necesarios y los carga en un DataTable.

Nota: MARS puede ser una solución, pero puede ser riesgoso ya muchas personas les disgusta.

Referencia

  1. Qué significa "Se produjo un error grave en el comando actual. Los resultados, si los hubiera, deben descartarse". ¿Error de SQL Azure?
  2. Linq-To-Sql y MARS woes: se ha producido un error grave en el comando actual. Los resultados, en su caso, deben ser descartados.
  3. Complejo GROUP BY en DataTable

Solo agrega lo siguiente en tu cadena de conexión:

MultipleActiveResultSets=True;


Sugiero crear una conexión adicional para el segundo comando, lo resolvería. Trate de combinar ambas consultas en una consulta. Crear una subconsulta para el conteo.

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

¿Por qué reemplazar el mismo valor una y otra vez?

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

Seria suficiente.