Kishore_Patel Ответов: 3

System.invalidoperationexception: "с этой командой уже связан открытый datareader, который должен быть закрыт первым".


Я хочу прочитать данные из 2 разных таблиц trxdat и trxitem

но при 2-м чтении я получаю ошибку

System.InvalidOperationException: 'There is already an open DataReader associated with this Command which must be closed first.'


команда и строка все разные

как мне работать с обоими читателями , потому что в каждой строке trxdat я должен читать связанные данные из tritem

Что я уже пробовал:

строку str1 = "выбрать * из таблицы dbo.trxdat где cmp_code = '" + SPGlobal.spcmp_code + " и trx_type = '" + RMSTБЫЛ + "'";
Sqlcommand, который mcmd = новая команда sqlcommand(str1 выглядит следующим образом, SPDConn);
SqlDataReader mrdr = mcmd.Метода executereader();
DataTable dt = новый DataTable();

строку str2 = "выбрать * из таблицы dbo.тритем где cmp_code = '" + SPGlobal.spcmp_code + " и trx_type = '" + RMSTБЫЛ + "и trx_no = '" + trx_no + "'";
SqlCommand icmd = new SqlCommand(str2, SPDConn);
SqlDataReader irdr = icmd.Метода executereader();

Richard MacCutchan

Вам нужно закрыть дубликат считывателя.

Richard Deeming

У вас есть гораздо большая проблема, чем это. Ваш код уязвим для SQL-инъекция[^]. НИКОГДА используйте конкатенацию строк для построения SQL-запроса. ВСЕГДА используйте параметризованный запрос.

Все, что вы хотели знать о SQL-инъекции (но боялись спросить) | Трой Хант[^]
Как я могу объяснить SQL-инъекцию без технического жаргона? | Обмен Стеками Информационной Безопасности[^]
Шпаргалка по параметризации запросов | OWASP[^]

j snooze

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

3 Ответов

Рейтинг:
1

Kishore_Patel

Окей
вместо чтения я должен использовать вставки с параметром
и установите первичный ключ для таблицы dt1
Я пытался использовать reader, потому что у меня было мало вычислений и манипуляций с данными

Спасибо за логику


Рейтинг:
0

Dave Kreskowiak

Послание означает именно то, что оно говорит.

Как только у вас есть DataReader, работающий на объекте соединения, это соединение не может делать ничего другого, например другой запрос, пока DataReader не будет закрыт.

Вам придется переделать свой код. Никто не может сказать вам, как это сделать, не догадываясь, потому что вы не сказали, что вы пытаетесь сделать, и код, который вы публикуете, немного запутан, не давая никакого реального направления.


Рейтинг:
0

MadMyche

Как уже говорилось ранее...
1. Ваш код уязвим для SQL-инъекций.
2. Ваша ошибка вызвана попыткой открыть одновременно 2 считывателя.
3. Вы объявляете DataTable, но никогда не используете его.
Заметить также...
4. Ваши запросы почти идентичны

Я предполагаю, что вы хотели бы, чтобы это заполнило пару таблиц данных из этих почти идентичных запросов.

Первое, что я собираюсь сделать, это объявить две таблицы данных для хранения результатов чтения.

DataTable dt1 = new DataTable();
DataTable dt2 = new DataTable();
Далее я объявлю 2 строки для запросов. Переменные будут заменены на Параметры
string qry1 = "SELECT * FROM dbo.trxdat WHERE cmp_code = @CmpCode AND trx_type = @TrxType";
string qry2 = "SELECT * FROM dbo.tritem WHERE cmp_code = @CmpCode AND trx_type = @TrxType AND trx_no = @TrxNo";
Далее мы объявим один объект Sql Command, который необходим для этого. Это будет обернуто внутри Using блок, который будет утилизировать и убирать его, когда мы закончим. Как только эта команда создана, мы можем добавить к ней параметры
using (SqlCommand cmd = new SqlCommand(qry1, SPDConn)) {
	cmd.Parameters.AddWithValue("@CmpCode", SPGlobal.spcmp_code);
	cmd.Parameters.AddWithValue("@TrxType", RMST);
Теперь мы создадим DataReader для выполнения команды и заполнения одной из таблиц данных. Это также будет обернуто в блок USING так что он будет закрыт и утилизирован как только мы закончим с ним
  using (SqlDataReader sdr = cmd.ExecuteReader()) {
      dt1.Load(sdr);
}
В этот момент, СДР больше не существует, поэтому соединение снова доступно для использования.
Наш объект SqlCommand все еще является жизнеспособным объектом, и поскольку он больше не используется, мы можем просто изменить необходимые нам свойства.
Текст команды необходимо будет изменить
Параметры являются частью коллекции, и потому те, которые заполняются, необходимы для второго запроса; все, что нам нужно сделать, это добавить последний.
cmd.CommandText = qry2;
cmd.Parameters.AddWithValue("@TrxNo", trx_no);
А потом повторить процесс - использование объекта sqldatareader пределах с помощью блока, чтобы заполнить вторую таблицу данными.
Последняя закрывающая скобка предназначена для блока Using, созданного для объекта Command, и это позволит правильно утилизировать объект CMD
	using (SqlDataReader sdr = cmd.ExecuteReader()) {
		dt2.Load(sdr);
	}
}