c# - être - multipleactiveresultsets=true




Erreur «Il existe déjà un DataReader ouvert associé à cette commande qui doit être fermé en premier» lors de l'utilisation de 2 commandes distinctes (5)

J'ai ce code hérité:

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


J'ai créé deux commandes distinctes pour corriger le problème, mais le problème persiste: "Un DataReader ouvert associé à cette commande doit déjà être fermé".

Une information supplémentaire: le même code fonctionne dans une autre application.


  1. La solution optimale pourrait être d’essayer de transformer votre solution en une forme dans laquelle vous n’auriez pas besoin d’avoir deux lecteurs ouverts à la fois. Idéalement, il pourrait s'agir d'une requête unique. Je n'ai pas le temps de le faire maintenant.
  2. Si votre problème est si spécial que vous avez réellement besoin d'ouvrir plus de lecteurs simultanément et que vos exigences n'autorisent pas le backend SQL antérieur à DB Server 2005, le mot magique est MARS (Multiple Active Result Set) . http://msdn.microsoft.com/en-us/library/ms345109%28v=SQL.90%29.aspx . La solution de la rubrique liée de Bob Vale montre comment l'activer: spécifiez MultipleActiveResultSets=true dans votre chaîne de connexion. Je dis simplement que c'est une possibilité intéressante, mais vous devriez plutôt transformer votre solution.

    • afin d'éviter la possibilité d'injection SQL mentionnée, définissez les paramètres sur la SQLCommand elle-même au lieu de les incorporer dans la chaîne de requête. La chaîne de requête ne doit contenir que les références aux paramètres que vous transmettez à la commande SqlCommand.

Ajoutez MultipleActiveResultSets=true à la partie fournisseur de votre chaîne de connexion. Voir l'exemple ci-dessous:

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

Essayez de combiner la requête, cela s'exécutera beaucoup plus rapidement que d'exécuter une requête supplémentaire par ligne. Ik n'aime pas la chaîne [] que vous utilisez, je créerais une classe pour contenir les informations.

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

Non testé, mais peut-être donne une idée.


Je parie que le problème est montré dans cette ligne

SqlDataReader dr3 = com2.ExecuteReader();

Je suggère que vous exécutiez le premier lecteur et faites un dr.Close(); et iterate historicos , avec une autre boucle, en exécutant la 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;

Vous pouvez avoir un tel problème lorsque vous utilisez two different commands sur la même connexion, en particulier lorsque vous appelez la deuxième commande dans une loop . Cela appelle la deuxième commande pour chaque enregistrement renvoyé par la première commande. Si la première commande renvoie environ 10 000 enregistrements, le problème sera probablement plus grave.

J'avais l'habitude d'éviter un tel scénario en le transformant en une seule commande. La première commande renvoie toutes les données requises et les charge dans un DataTable.

Remarque: MARS peut être une solution - mais cela peut être risqué et beaucoup de gens ne l'aiment pas.

Référence

  1. "Une erreur grave s'est produite sur la commande en cours. Les résultats, le cas échéant, doivent être ignorés." Erreur SQL Azure signifie?
  2. Malheurs Linq-To-Sql et MARS - Une erreur grave s'est produite sur la commande en cours. Les résultats, le cas échéant, doivent être écartés
  3. GROUP BY complexe sur DataTable




c#