Подтвердить что ты не робот

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

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

4b9b3361

Ответ 1

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

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
}

Было бы достаточно.

Ответ 2

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

MultipleActiveResultSets=True;

Ответ 3

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

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

Ответ 4

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

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

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

Ссылка

Ответ 5

Держу пари, что проблема показана в этой строке

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;

Ответ 6

Попробуйте объединить запрос, он будет работать намного быстрее, чем выполнение дополнительного запроса в строке. 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 дает некоторую идею.

Ответ 7

Добавьте 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" />