Существует назначенный этой команде command открытый datareader

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

Описание: Необработанное исключение при выполнении текущего веб-запроса. Изучите трассировку стека для получения дополнительных сведений о данной ошибке и о вызвавшем ее фрагменте кода.

Сведения об исключении: System.InvalidOperationException: Существует назначенный этой команде открытый DataReader, который требуется предварительно закрыть.

Код ресурса, сгенерировавший это необработанное исключение, может быть отображен только после компиляции в режиме отладки. Для этого выполните одно из указанных ниже действий, а затем перейдите по следующему URL-адресу:

1. Добавьте директиву "Debug=true" в начало файла, сгенерировавшего ошибку. Пример:

2) Добавьте следующий раздел в файл конфигурации приложения:

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

позволяет выполнить SQL-оператор любого типа. Хотя класс Command можно использовать для решения задач определения данных (таких как создание и изменение баз данных, таблиц и индексов), все же более вероятно его применение для выполнения задач манипулирования данными (вроде извлечения и обновления записей в таблице).

Специфичные для поставщика классы Command реализуют стандартную функциональность, в точности как классы Connection. В данном случае небольшой набор ключевых свойств и методов, используемых для выполнения команд через открытое соединение, определяется интерфейсом IDbCommand.

Основные сведения о командах

Прежде чем использовать команду, необходимо выбрать ее тип, установить ее текст и привязать к соединению. Всю эту работу можно выполнить, установив значения соответствующих свойств (CommandType, CommandText и Connection), либо передать необходимую информацию в аргументах конструктора.

Текстом команды может быть SQL-оператор, хранимая процедура либо имя таблицы. Это зависит от типа используемой команды. Существуют три типа команд, которые описаны в таблице:

Значения перечисления CommandType
Значение Описание
CommandType.Text

Команда будет выполнять прямой оператор SQL. Оператор SQL указывается в свойстве CommandText. Это — значение по умолчанию

CommandType.StoredProcedure

Команда будет выполнять хранимую процедуру в источнике данных. Свойство CommandText представляет имя хранимой процедуры

CommandType.TableDirect

Команда будет опрашивать все записи таблицы. CommandText — имя таблицы, из которой команда извлечет все записи. (Эта опция предназначена только для обратной совместимости с некоторыми драйверами OLE DB. Она не поддерживается поставщиком данных SQL Server и не работает так хорошо, как тщательно направленный запрос.)

Например, ниже показано, как создать объект Command, представляющий запрос:

А вот более эффективный способ использования одного конструктора Command. Обратите внимание, что указывать CommandType не нужно, поскольку значение CommandType.Text принимается по умолчанию:

В этих примерах объект Command просто определяется, но не выполняется. Объект Command предоставляет три метода, которые можно использовать для выполнения команды, в зависимости от того, нужно ли вам извлечь полный результирующий набор, получить единственное значение или просто выполнить отличную от запроса команду. Эти методы перечислены в таблице ниже:

Методы Command
Метод Описание ExecuteNonQuery()

Выполняет команды, отличные от select, такие как SQL-операторы вставки, удаления или обновления записей. Возвращаемое значение указывает количество строк, обработанных командой. Метод ExecuteNonQuery() также можно использовать для выполнения команд определения данных, которые создают, изменяют и уничтожают объекты базы данных (наподобие таблиц, индексов, ограничений и т.п.)

ExecuteScalar()

Выполняет запрос select и возвращает значение первого поля первой строки из набора строк, сгенерированного командой. Этот метод обычно применяется при выполнении агрегатной команды select, использующей функции вроде count() или sum() для вычисления единственного значения

ExecuteReader()

Выполняет запрос select и возвращает объект DataReader, который является оболочкой однонаправленного курсора, доступного только для чтения

Чуть позже я продемонстрирую использование этих методов, но для начала нужно разобрать класс DataReader, экземпляр которого возвращает метод ExecuteReader().

Класс DataReader

Класс DataReader позволяет читать данные, возвращенные командой SELECT, по одной строке за раз, в однонаправленном, доступном только для чтения потоке. Иногда это называют курсором. Использование DataReader — простейший путь получения данных, но ему недостает возможностей сортировки и связывания автономного объекта DataSet. Однако DataReader представляет наиболее быстрый способ доступа к данным. Ниже перечислены основные методы DataReader:

Методы класса DataReader
Метод Описание Read()

Перемещает курсор строки на следующую строку в потоке. Этот метод также должен быть вызван перед чтением первой строки данных (Когда DataReader создается впервые, курсор строки помещается в позицию непосредственно перед первой строкой.) Метод Read() возвращает true, если существует следующая строка для чтения, или false, если прочитана последняя строка в наборе

GetValue()

Возвращает значение, сохраненное в поле с указанным именем столбца или индексом, внутри текущей выбранной строки. Тип возвращенного значения — ближайший тип .NET, наиболее соответствующий встроенному значению, хранимому в источнике данных. Если вы обратитесь к полю по индексу и нечаянно передадите неверный индекс, ссылающийся на несуществующее поле, то получите исключение IndexOutOfRangeException. Используя индексатор для DataReader, можно получить значение по имени поля

Читайте также:  Игра kingdom come deliverance системные требования
GetValues()

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

GetInt32(), GetChar(), GetDateTime(), Get. ()

Эти методы возвращают значение поля с указанным индексом в текущей строке, причем тип данных указывается в имени метода. Обратите внимание, что если попытаться присвоить возвращенное значение переменной неверного типа, возникнет исключение InvalidCastException. Кроме того, эти методы не поддерживают типов, допускающих NULL-значения.

Если поле может содержать null, это придется проверить перед вызовом одного из методов. Чтобы проверить на null-значение, сравните непреобразованное значение (которое можно извлечь по позиции методом GetValue() или по имени с помощью индексатора DataReader) с константой DBNull.Value

NextResult()

Если команда, которая сгенерировала DataReader, возвратила более одного набора строк, этот метод перемещает указатель на следующую строку и устанавливает его непосредственно перед первой строкой

Close()

Закрывает модуль чтения. Если исходная команда запустила хранимую процедуру, возвратившую выходное значение, это значение может быть прочитано из соответствующего параметра после закрытия модуля чтения

Метод ExecuteReader() и DataReader

В следующем примере создается простая команда запроса, которая должна вернуть все записи из таблицы Employees базы данных Northwind. Команда создается при загрузке страницы. Соединение открывается, и команда выполняется методом ExecuteReader(), который возвращает SqlDataReader. Получив DataReader, можно организовать цикл для прохождения по его записям, вызывая метод Read() в теле цикла. Этот метод перемещает курсор строки на следующую запись (при первом вызове — на первую строку). Метод Read() также возвращает булевское значение, означающее наличие последующих строк для чтения. В следующем примере цикл продолжается до тех пор, пока Read() не вернет false, после чего элегантно завершается:

Обратите внимание, что этот код читает значение поля TitleOfCourtesy, обращаясь к нему по имени, через индексатор Item. Поскольку свойство Item — индексатор по умолчанию, явно включать имя свойства Item при извлечении значения поля не понадобится. Далее код читает поля LastName и FirstName, вызывая GetString() с индексом поля (1 и 2 в данном случае). И, наконец, код обращается к полю HireDate, вызывая метод GetDateTime() с индексом поля, равным 6. Все эти подходы эквивалентны и включены сюда для демонстрации поддерживаемых вариантов.

Запустив эту страницу, вы увидите результат, показанный на рисунке ниже:

В большинстве страниц ASP.NET столь трудоемкий подход для отображения данных использовать не придется. Вместо этого вы будете применять элементы управления данными, которые описаны в последующих статьях. Однако DataAdapter, скорее всего, будет использоваться при написании кода доступа к данным в компоненте базы данных.

Значения null

Как вам уже наверняка известно, базы данных используют значения null для представления отсутствующей или неопределенной информации. Эту же концепцию можно использовать в .NET с типами, допускающими значения null, которые принимают значения и null-ссылки. Ниже приведен пример с целочисленной переменной, допускающей значение null:

К сожалению, DataReader не интегрируется с допускающими null значениями .NET. Это расхождение объясняется причинами исторического характера. Допускающие null типы данных впервые появились в .NET 2.0, когда модель DataReader уже хорошо устоялась, и ее было трудно менять.

Вместо этого DataReader возвращает константу DBNull.Value, когда встречает в базе данных значение null. Попытка использовать это значение или привести его к другому типу данных вызовет исключение. (Печально, но никакого способа приведения между DBNull.Value и типами, допускающими null, не существует.) В результате должна предприниматься проверка на DBNull.Value там, где это может возникнуть, с использованием следующего кода:

Перечисление CommandBehavior

Метод ExecuteReader() имеет перегруженную версию, которая принимает в качестве параметра значение перечисления CommandBehavior. Одно из часто используемых его значений — CommandBehavior.CloseConnection. Когда это значение передается ExecuteReader(), то DataReader закрывает ассоциированное с ним соединение, как только закрывается сам DataReader (при использовании конструкции using в применении этой опции нет смысла).

Другое допустимое значение — CommandBehavior.SingleRow — повышает производительность выполнения запроса, когда нужно извлечь единственную строку. Например, если вы извлекаете единственную запись, используя уникальное значение первичного ключа (CustomerID, Production и т.д.), то можно применить эту оптимизацию. Можно также использовать CommandBehavior.SequentialAccess для чтения части двоичного поля, что снижает расход памяти при чтении больших двоичных полей.

Другие значения перечисления применяют реже, поэтому здесь не рассматриваются. Полный список можно найти в документации по .NET.

Читайте также:  Искать исполнителя по музыке

Обработка множества результирующих наборов

Выполняемая команда не обязательно должна возвращать единственный результирующий набор. Она может выполнить более одного запроса и вернуть более одного результирующего набора. Это удобно для извлечения большого объема взаимосвязанных данных, например, списка товаров и их категорий, которые вместе представляют каталог товаров.

Команда может вернуть более одного результирующего набора двумя способами:

Если вызывается хранимая процедура, она может содержать несколько операторов SELECT.

Если вы используете простую текстовую команду, то можете собрать вместе несколько SQL-команд, разделяя их точками с запятой (;). Не все поставщики данных поддерживают такой прием, однако поставщик SQL Server это делает.

Ниже приведен пример строки, определяющей пакет из трех команд select:

Строка содержит три запроса. Все вместе они возвращают первые пять записей из таблицы Employees, первые пять записей из таблицы Customers и первые пять — из таблицы Suppliers. Обработка результатов достаточно проста. Вначале DataReader обеспечивает доступ к результатам из таблицы Employees. По завершении чтения методом Read() всех этих записей вызывается метод NextResult() для перехода к следующему результирующему набору. Когда больше не остается результирующих наборов, этот метод вернет false.

Проход по всем результирующим наборам можно организовать в цикле while, хотя в этом случае нужно проявить осторожность и не вызвать NextResult() до завершения чтения первого результирующего набора. Ниже показан пример применения этого более специализированного приема:

Обратите внимание, что в этом случае доступ ко всем полям осуществляется с использованием обобщенного метода GetValue(), который принимает индекс читаемого поля. Причина в том, что код спроектирован для чтения всех полей всех возвращаемых результирующих наборов, независимо от того, какой запрос применяется в каждом случае. Однако в реальном приложении базы данных вы почти наверняка будете знать имена и типы всех полей всех таблиц.

Метод ExecuteScalar()

Метод ExecuteScalar() возвращает значение сохраненной в первом поле первой строки результирующего набора, сгенерированного запросом SELECT команды. Этот метод обычно применяется для выполнения запросов, возвращающих единственное поле, возможно, вычисленное агрегатной функцией SQL вроде COUNT() или SUM().

Следующая процедура демонстрирует, как можно с таким подходом получить (и отобразить на странице) количество записей таблицы Employees:

Код достаточно прост, но стоит отметить, что вы должны привести возвращаемое значение к правильному типу, поскольку ExecuteScalar() возвращает объект.

Метод ExecuteNonQuery()

Метод ExecuteNonQuery() выполняет команды, которые не возвращают результирующих наборов, такие как INSERT, DELETE или UPDATE. Метод ExecuteNonQuery() возвращает одну порцию информации — количество обработанных записей (или -1, если команда отлична от INSERT, DELETE или UPDATE). Применение этого метода класса Command аналогично предыдущим методам.

у меня есть этот запрос, и я получаю ошибку в этой функции:

там уже открытый DataReader, связанный с этой командой, которая должна быть закрыта.

обновление:

трассировка стека добавлено:

16 ответов:

Это может произойти, если вы выполняете запрос во время итерации по результатам другого запроса. Из вашего примера не ясно, где это происходит, потому что пример не является полным.

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

Это можно легко решить, разрешив MARS в строке подключения. Добавить MultipleActiveResultSets=true к поставщику часть строки подключения (где источник данных, исходный каталог, так далее. уточняются).

можно использовать ToList() способ перед return заявление.

вот рабочая строка подключения для тех, кто нуждается в ссылке.

в моем случае, используя Include() решена эта ошибка и в зависимости от ситуации может быть гораздо более эффективным, чем выдача нескольких запросов, когда все это может быть запрошено сразу с помощью соединения.

использовать следующий синтаксис: .ToList() чтобы преобразовать объект чтения из БД в список, чтобы избежать повторного чтения снова.Надеюсь, это сработает. Спасибо.

Я не знаю, является ли это дубликат ответа или нет. Если это так, то мне очень жаль. Я просто хочу, чтобы нуждающиеся знали, как я решил свою проблему с помощью ToList().

в моем случае я получил такое же исключение для запроса ниже.

Я решил, как ниже

похоже, что вы вызываете DateLastUpdated из активного запроса, используя тот же контекст EF, и DateLastUpdate выдает команду самому хранилищу данных. Entity Framework поддерживает только одну активную команду для каждого контекста одновременно.

вы можете разложить ваши выше двух запросов в один, как этот:

Я также заметил, что вы вызываете такие функции, как FormattedAccountNumber и FormattedRecordNumber в запросах. Если эти хранимые процедуры или функции, которые вы импортировали из своей базы данных в модель данных сущности и правильно сопоставили, они также будут выбрасывать исключения, поскольку EF не будет знать, как перевести эти функции в операторы, которые он может отправить в хранилище данных.

Читайте также:  Мтс срочно позвонить оператору

также обратите внимание, что вызов AsEnumerable не заставляет запрос выполняться. Пока выполнение запроса не будет отложено до перечисления. Вы можете принудительно перечислить с помощью ToList или ToArray, если хотите.

кроме Ладислав Мрнка ответ:

Если вы публикуете и переопределяете контейнер на настройки tab, вы можете установить MultipleActiveResultSet значение true. Вы можете найти эту опцию, нажав дополнительно. и будет в Advanced группы.

У меня была такая же ошибка, когда я пытался обновить некоторые записи в цикле чтения. Я пробовал самый проголосовавший ответ MultipleActiveResultSets=true и обнаружил, что это просто обходной путь, чтобы получить следующий ошибка

новая транзакция не разрешена, потому что есть другие запущенные потоки в сессии

лучший подход, который будет работать для огромных наборов результатов, – это использовать куски и открывать отдельный контекст для каждого куска, как описано в разделе SqlException от сущности Framework-новая транзакция не разрешена, потому что в сеансе работают другие потоки

Я решил эту проблему путем изменения жду _accountSessionDataModel.SaveChangesAsync(); к _accountSessionDataModel.SaveChanges(); в моем классе репозитория.

проблема заключалась в том, что я обновил сеансы в интерфейсе после создания сеанса (в коде), но поскольку SaveChangesAsync происходит асинхронно, выборка сеансов вызвала эту ошибку, потому что, по-видимому, операция SaveChangesAsync еще не была готова.

для тех, кто находит это через Google;
Я получал эту ошибку, потому что, как было предложено ошибкой, мне не удалось закрыть SqlDataReader до создания другого в том же SqlCommand, ошибочно предполагая, что это будет мусор, собранный при выходе из метода, в котором он был создан.

Я решил проблему путем вызова sqlDataReader.Close(); перед созданием второго считывателя.

в моем случае, я открыл запрос из контекста данных, как

. а затем спросил же.

добавлять .ToList чтобы первый решил мою проблему. Я думаю, что имеет смысл обернуть это в свойство, как:

где _stores-это частная переменная, а Filters-это также свойство только для чтения, которое считывается из AppSettings.

Я использую веб-службу в своем инструменте, где эти службы извлекают хранимую процедуру. в то время как большее количество клиентских инструментов извлекает веб-службу, эта проблема возникает. Я исправил, указав атрибут Synchronized для тех функций, которые извлекают хранимую процедуру. теперь он работает нормально, ошибка никогда не появлялась в моем инструменте.

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

Ну для меня это был мой собственный баг. Я пытался запустить INSERT используя SqlCommand.executeReader() когда я должен был с помощью SqlCommand.ExecuteNonQuery() . Он был открыт и никогда не закрывался, вызывая ошибку. Следите за этой оплошностью.

Это извлекается из реального сценария мира:

  • код хорошо работает в среде этапа с MultipleActiveResultSets устанавливается в строке подключения
  • код, опубликованный в рабочей среде без MultipleActiveResultSets=true
  • так много страниц / вызовов работают, пока один из них не работает
  • глядя ближе на вызов, есть ненужный вызов сделано в БД и должно быть удалено
  • Set MultipleActiveResultSets=true в производстве и публикации очищенного кода, все работает хорошо и эффективно

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

скорее всего, эта проблема возникает из-за "ленивой загрузки" функции Entity Framework. Обычно, если явно не требуется во время начальной выборки, все объединенные данные (все, что хранится в других таблицах базы данных) извлекаются только при необходимости. Во многих случаях это хорошо, так как это предотвращает извлечение ненужных данных и, таким образом, повышает производительность запросов (без соединений) и экономит пропускную способность.

в ситуации, описанной в вопросе, выполняется начальная выборка, и во время фазы" select "запрашиваются отсутствующие данные ленивой загрузки, выдаются дополнительные запросы, а затем EF жалуется на"open DataReader".

обходной путь, предложенный в принятом ответе, позволит выполнить эти запросы, и действительно весь запрос будет успешным.

однако, если вы изучите запросы, отправленные в базу данных, вы заметите несколько запросов – дополнительный запрос для каждого отсутствующего (лениво загруженного) данных. Это может быть представление убийца.

лучший подход-сказать EF, чтобы предварительно загрузить все необходимые ленивые загруженные данные во время начального запроса. Это можно сделать с помощью оператора" Include":

таким образом, все необходимые соединения будут выполнены и все необходимые данные будут возвращены как один запрос. Проблема, описанная в вопросе, будет решена.