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