71

私はこのレガシーコードを持っています:

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


この問題を修正するために 2 つの別個のコマンドを作成しましたが、「このコマンドに関連付けられた開いている DataReader が既に存在し、最初に閉じる必要があります」というメッセージが引き続き表示されます。

追加情報: 同じコードが別のアプリで動作しています。

4

7 に答える 7

21
  1. 最適な解決策は、一度に 2 つのリーダーを開く必要がない形式にソリューションを変換しようとすることです。理想的には、単一のクエリである可能性があります。今はそれをする時間がありません。
  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 に渡すパラメーターへの参照のみを含める必要があります。
于 2013-08-27T21:08:45.107 に答える
8

2番目のコマンドに追加の接続を作成することをお勧めします。これで解決します。両方のクエリを 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
}

十分でしょう。

于 2013-08-27T20:52:26.943 に答える
8

問題はこの行に表示されているに違いない

SqlDataReader dr3 = com2.ExecuteReader();

最初のリーダーを実行して adr.Close();と iteratehistoricosを実行し、別のループで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;
于 2013-08-27T20:53:02.807 に答える
3

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" />
于 2017-09-12T10:30:04.483 に答える