Program Tip

2 개의 고유 한 명령을 사용할 때 "이 명령과 관련된 열린 DataReader가 이미 있으며 먼저 닫아야합니다."오류

programtip 2020. 11. 12. 20:08
반응형

2 개의 고유 한 명령을 사용할 때 "이 명령과 관련된 열린 DataReader가 이미 있으며 먼저 닫아야합니다."오류


이 레거시 코드가 있습니다.

 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가 이미 있습니다."

추가 정보 : 동일한 코드가 다른 앱에서 작동하고 있습니다.


두 번째 명령에 대한 추가 연결을 만드는 것이 좋습니다. 두 쿼리를 하나의 쿼리로 결합하십시오. 개수에 대한 하위 쿼리를 만듭니다.

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
}

충분할 것입니다.


연결 문자열에 다음을 추가하기 만하면됩니다.

MultipleActiveResultSets=True;

  1. 최적의 솔루션은 한 번에 두 명의 독자를 열 필요가없는 형태로 솔루션변환 하는 것입니다. 이상적으로는 단일 쿼리 일 수 있습니다. 지금 할 시간이 없습니다.
  2. 문제가 너무 특별해서 동시에 더 많은 리더를 열어야하고 요구 사항이 SQL Server 2005 DB 백엔드 이전 버전을 허용하지 않는 경우 마법 단어는 MARS (Multiple Active Result Sets) 입니다. http://msdn.microsoft.com/en-us/library/ms345109%28v=SQL.90%29.aspx . Bob Vale의 연결된 주제의 솔루션은이를 활성화하는 방법을 보여줍니다 MultipleActiveResultSets=true. 연결 문자열에 지정 합니다. 나는 이것을 흥미로운 가능성으로 말하지만 오히려 솔루션을 변환해야합니다.

    • 언급 된 SQL 삽입 가능성을 방지하려면 매개 변수를 쿼리 문자열에 포함하는 대신 SQLCommand 자체에 설정하십시오. 쿼리 문자열에는 SqlCommand에 전달하는 매개 변수에 대한 참조 만 포함되어야합니다.

two different commands같은 연결에 있을 때 이러한 문제가 발생할 수 있습니다. 특히 loop. 즉, 첫 번째 명령에서 반환 된 각 레코드에 대해 두 번째 명령을 호출합니다. 첫 번째 명령에서 반환 된 약 10,000 개의 레코드가있는 경우이 문제가 발생할 가능성이 더 높습니다.

저는 이러한 시나리오를 단일 명령으로 만들어서 피했습니다. 첫 번째 명령은 필요한 모든 데이터를 반환하고 DataTable에로드합니다.

참고 : MARS해결책이 될 수 있지만 위험 할 수 있으며 많은 사람들이 싫어합니다.

참고

  1. "현재 명령에 심각한 오류가 발생했습니다. 결과가있는 경우 삭제해야합니다." SQL Azure 오류는 무엇입니까?
  2. Linq-To-Sql 및 MARS 문제-현재 명령에서 심각한 오류가 발생했습니다. 결과 (있는 경우)를 삭제해야합니다.
  3. DataTable의 복잡한 GROUP BY

이 줄에 문제가 표시되고 있다고 확신합니다.

SqlDataReader dr3 = com2.ExecuteReader();

첫 번째 판독기를 실행하고 a 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;

쿼리를 결합하면 행당 추가 쿼리를 실행하는 것보다 훨씬 빠르게 실행됩니다. 당신이 사용하고있는 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;
    }

테스트되지 않았지만 maybee가 몇 가지 아이디어를 제공합니다.


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

참고URL : https://stackoverflow.com/questions/18475195/error-there-is-already-an-open-datareader-associated-with-this-command-which-mu

반응형