2013-08-27 2 views
57

У меня есть этот унаследованный код:Ошибка «Существует уже открытая DataReader, связанная с этой командой, которая должна быть закрыта первой» при использовании 2 различных команд

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, связанный с этой Командой, который должен быть закрыт первым».

Дополнительная информация: тот же код работает в другом приложении.

+21

У вас есть уязвимость в SQL-инъекции. – SLaks

+0

Возможный дубликат [Существует уже открытый DataReader, связанный с этой Командой, который должен быть закрыт первым] (http://stackoverflow.com/questions/6062192/there-is-already-an-open-datareader-associated-with- this-command-which-must-be-c) –

+3

Это не просто команда, это соединение. Если вы используете одно и то же соединение для обеих команд, вы получите сообщение об ошибке. –

ответ

7

Предлагаю создать дополнительное соединение для второй команды, решив ее. Попробуйте объединить оба запроса в одном запросе. Создайте подзапрос для подсчета.

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 
} 

Будет достаточно.

+5

создание второго соединения с одной базой данных, возможно, решает проблему, но не помогает считывать и обслуживать код –

+0

+1. Таким образом, он мог быть независимым от поставщика серверов, хотя для этого он должен использовать DbConnection вместо SqlConnection и Db * classes isnetad любого класса Sql *. Но лучше всего, если он переписывает свои запросы, как и вы (и любой ответ). –

+0

Надеюсь, это будет полезно [Ответ] (http://stackoverflow.com/a/6064422/4471013) –

9

небось проблема показывается в этой строке

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; 
+1

Думаю, он не может закрыть его, потому что тогда он не может его прочитать. –

+0

@CsabaToth две петли для чтения данных в dados_historico и закройте читателя, а затем еще один цикл для выполнения другого читателя. –

+0

+1 Я знаю. И, похоже, он собирает данные в какой-то массив, поэтому он может его повторно использовать. Твое - это реальное решение, если нет еще лучшего одного запроса. –

18
  1. Оптимальное решение может быть, чтобы попытаться превратить ваше решение в форму, где вам не нужно иметь два считывателя открытых одновременно. В идеале это может быть один запрос. У меня нет времени на это.
  2. Если ваша проблема настолько особенная, что вам действительно нужно открывать больше читателей одновременно, а ваши требования позволяют не раньше, чем бэкенд базы данных SQL Server 2005, тогда магическое слово: MARS (несколько активных наборов результатов). http://msdn.microsoft.com/en-us/library/ms345109%28v=SQL.90%29.aspx. Решение связанного раздела Bob Vale показывает, как включить его: укажите в строке подключения MultipleActiveResultSets=true. Я просто говорю это как интересную возможность, но вы должны скорее преобразовать свое решение.

    • , чтобы избежать упомянутой возможности SQL-инъекции, задайте параметры самому SQLCommand, а не вставляйте их в строку запроса. Строка запроса должна содержать только ссылки на параметры, которые вы передаете в SqlCommand.
+0

Csaba Toth работает с настройками MARS в другой базе данных, которая не является MS SQL SERVER? –

+0

Это функция MS SQL Server. 2005 и выше. Другие СУРБД могут иметь аналогичную функцию, но специфичные для конкретного типа сервера. –

+1

Также могут быть и недостатки: http://stackoverflow.com/questions/374444/disadvantages-of-mars-multiple-active-result-sets –

3

Попытка объединить запрос, он будет работать намного быстрее, чем выполнение дополнительного запроса в строке. Ik не нравится строка [], которую вы используете, я бы создал класс для хранения информации.

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

Непрошеный, но maybee дает некоторую идею.

10

У вас может возникнуть такая проблема, когда вы используете two different commands при одном и том же соединении - особенно при вызове второй команды в loop. Это вызывает вторую команду для каждой записи, возвращенной из первой команды.Если есть 10 000 записей, возвращенных первой командой, эта проблема будет более вероятной.

Я использовал, чтобы избежать такого сценария, сделав его как единую команду. Первая команда возвращает все необходимые данные и загружает их в DataTable.

Примечание: MARS может быть решением, но это может быть опасно, и многим это не нравится.

Ссылка

  1. What does "A severe error occurred on the current command. The results, if any, should be discarded." SQL Azure error mean?
  2. Linq-To-Sql and MARS woes - A severe error occurred on the current command. The results, if any, should be discarded
  3. Complex GROUP BY on DataTable
+0

ключ: если вы вызываете foreach() в IQueryable (вы сделали .Where(), но не следовали ему с .ToList()), а затем попытаетесь использовать контекст в этом цикле, это может вызвать проблему. – mmcrae

189

Просто добавьте следующую строку в вашей строке соединения:

MultipleActiveResultSets=True; 
+3

http://stackoverflow.com/questions/374444/disadvantages-of-mars-multiple-active-result-sets – Amicable

+6

Должен быть принятый ответ. –

1

Добавить 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" /> 
Смежные вопросы

 Смежные вопросы