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

641

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

var accounts = from account in context.Accounts
               from guranteer in account.Gurantors
               select new AccountsReport
               {
                   CreditRegistryId = account.CreditRegistryId,
                   AccountNumber = account.AccountNo,
                   DateOpened = account.DateOpened,
               };

 return accounts.AsEnumerable()
                .Select((account, index) => new AccountsReport()
                    {
                        RecordNumber = FormattedRowNumber(account, index + 1),
                        CreditRegistryId = account.CreditRegistryId,
                        DateLastUpdated = DateLastUpdated(account.CreditRegistryId, account.AccountNumber),
                        AccountNumber = FormattedAccountNumber(account.AccountType, account.AccountNumber)
                    })
                .OrderBy(c=>c.FormattedRecordNumber)
                .ThenByDescending(c => c.StateChangeDate);


public DateTime DateLastUpdated(long creditorRegistryId, string accountNo)
{
    return (from h in context.AccountHistory
            where h.CreditorRegistryId == creditorRegistryId && h.AccountNo == accountNo
            select h.LastUpdated).Max();
}

Ошибка:

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

Обновить:

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

InvalidOperationException: There is already an open DataReader associated with this Command which must be closed first.]
   System.Data.SqlClient.SqlInternalConnectionTds.ValidateConnectionForExecute(SqlCommand command) +5008639
   System.Data.SqlClient.SqlConnection.ValidateConnectionForExecute(String method, SqlCommand command) +23
   System.Data.SqlClient.SqlCommand.ValidateCommand(String method, Boolean async) +144
   System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, DbAsyncResult result) +87
   System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method) +32
   System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method) +141
   System.Data.SqlClient.SqlCommand.ExecuteDbDataReader(CommandBehavior behavior) +12
   System.Data.Common.DbCommand.ExecuteReader(CommandBehavior behavior) +10
   System.Data.EntityClient.EntityCommandDefinition.ExecuteStoreCommands(EntityCommand entityCommand, CommandBehavior behavior) +443

[EntityCommandExecutionException: An error occurred while executing the command definition. See the inner exception for details.]
   System.Data.EntityClient.EntityCommandDefinition.ExecuteStoreCommands(EntityCommand entityCommand, CommandBehavior behavior) +479
   System.Data.Objects.Internal.ObjectQueryExecutionPlan.Execute(ObjectContext context, ObjectParameterCollection parameterValues) +683
   System.Data.Objects.ObjectQuery`1.GetResults(Nullable`1 forMergeOption) +119
   System.Data.Objects.ObjectQuery`1.System.Collections.Generic.IEnumerable<T>.GetEnumerator() +38
   System.Linq.Enumerable.Single(IEnumerable`1 source) +114
   System.Data.Objects.ELinq.ObjectQueryProvider.<GetElementFunction>b__3(IEnumerable`1 sequence) +4
   System.Data.Objects.ELinq.ObjectQueryProvider.ExecuteSingle(IEnumerable`1 query, Expression queryRoot) +29
   System.Data.Objects.ELinq.ObjectQueryProvider.System.Linq.IQueryProvider.Execute(Expression expression) +91
   System.Data.Entity.Internal.Linq.DbQueryProvider.Execute(Expression expression) +69
   System.Linq.Queryable.Max(IQueryable`1 source) +216
   CreditRegistry.Repositories.CreditRegistryRepository.DateLastUpdated(Int64 creditorRegistryId, String accountNo) in D:\Freelance Work\SuperExpert\CreditRegistry\CreditRegistry\Repositories\CreditRegistryRepository.cs:1497
   CreditRegistry.Repositories.CreditRegistryRepository.<AccountDetails>b__88(AccountsReport account, Int32 index) in D:\Freelance Work\SuperExpert\CreditRegistry\CreditRegistry\Repositories\CreditRegistryRepository.cs:1250
   System.Linq.<SelectIterator>d__7`2.MoveNext() +198
   System.Linq.Buffer`1..ctor(IEnumerable`1 source) +217
   System.Linq.<GetEnumerator>d__0.MoveNext() +96
DotnetSparrow
источник

Ответы:

1288

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

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

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

Ладислав Мрнка
источник
34
Это сработало для меня. Если вы хотите узнать больше о включении нескольких активных наборов результатов (MARS), см. Msdn.microsoft.com/en-us/library/h32h3abf(v=vs.100).aspx . Вы можете прочитать на Недостатки MARS слишком stackoverflow.com/questions/374444/...
Diganta Кумар
3
Принимая во внимание производительность, вы также можете решить эту проблему, включив System.Data.Entity, а затем используя операторы Include, чтобы убедиться, что эти вторичные данные загружены в исходный запрос. Если вы включите MARS, отключив его для проверки этих повторяющихся загрузок данных, можно ускорить обработку вызовов, сократив количество обращений.
Крис Москини
70
Включение MARS должно быть сделано только для очень небольшого набора проблем / вариантов использования. В большинстве случаев рассматриваемая ошибка вызвана BAD CODE внутри вызывающего приложения. Более подробная информация здесь: devproconnections.com/development/…
Майкл К. Кэмпбелл,
132
Добавление .ToList () после your.Include (). Where (), скорее всего, решит проблему.
Серж Саган
2
Глобальное изменение SQL-соединения для одного запроса смешно. Правильный ответ должен быть представлен ниже. Локальное исправление (то есть просто изменение запроса) для локализованной проблемы!
bytedev
218

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

var accounts =
from account in context.Accounts
from guranteer in account.Gurantors

 select new AccountsReport
{
    CreditRegistryId = account.CreditRegistryId,
    AccountNumber = account.AccountNo,
    DateOpened = account.DateOpened,
};

 return accounts.AsEnumerable()
               .Select((account, index) => new AccountsReport()
                       {
                           RecordNumber = FormattedRowNumber(account, index + 1),
                           CreditRegistryId = account.CreditRegistryId,
                              DateLastUpdated = DateLastUpdated(account.CreditRegistryId, account.AccountNumber),
                           AccountNumber = FormattedAccountNumber(account.AccountType, account.AccountNumber)}).OrderBy(c=>c.FormattedRecordNumber).ThenByDescending(c => c.StateChangeDate).ToList();


 public DateTime DateLastUpdated(long creditorRegistryId, string accountNo)
    {
        var dateReported = (from h in context.AccountHistory
                            where h.CreditorRegistryId == creditorRegistryId && h.AccountNo == accountNo
                            select h.LastUpdated).Max();
        return dateReported;
    }
Казем
источник
9
У меня была эта ошибка очень много раз ... и каждый раз, когда я забываю! Ответом на вопрос всегда является использование ToList ().
Тост Cheesus
1
Есть ли минусы в этом? Если у вас есть 100 000 строк, я сомневаюсь, что это может быть хорошо.
Мартин Доусон
2
@MartinMazzaDawson, вам действительно нужно 100К записей сразу для выполнения запроса ?? я думаю, что использование пагинации - хорошая идея для этой ситуации
kazem
Извините, что поднял старую тему, но я столкнулся с той же ошибкой при разработке RepositoryPattern и решил ее, добавив ".ToList () или Single () или Count ()" к каждому методу репозитория. Хотя в начале я просто возвращал ".AsEnumerable ()". Теперь мой вопрос: должен ли репозиторий возвращать «ToList ()», или это то, что должно потребоваться конечному потребителю (т.
Е.
Работает для меня. Добавление .ToList решает проблему с поддержкой десятичной дроби в JetEntityFrameworkProvider. Total = storeDb.OF_Carts.Where(x => x.CartId == ShoppingCartId).ToList().Sum(t => t.Quantity * t.Item.UnitPrice);
hubert17
39

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

Icemark Muturi
источник
22

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

  <connectionStrings>
    <add name="IdentityConnection" connectionString="Data Source=(LocalDb)\v11.0;AttachDbFilename=|DataDirectory|\IdentityDb.mdf;Integrated Security=True;MultipleActiveResultSets=true;" providerName="System.Data.SqlClient" />
  </connectionStrings>
Ян Чжан
источник
15
Включение MARS - это обходной путь, а НЕ решение проблемы.
SandRock
5
Со страницы документации MARS: «Операции MARS не являются потокобезопасными». Это означает, что если проблема возникает из-за нескольких потоков, обращающихся к контексту, MARS (вероятно) не является решением.
marsop
20

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

IEnumerable<User> users = db.Users.Include("Projects.Tasks.Messages");

foreach (User user in users)
{
    Console.WriteLine(user.Name);
    foreach (Project project in user.Projects)
    {
        Console.WriteLine("\t"+project.Name);
        foreach (Task task in project.Tasks)
        {
            Console.WriteLine("\t\t" + task.Subject);
            foreach (Message message in task.Messages)
            {
                Console.WriteLine("\t\t\t" + message.Text);
            }
        }
    }
}
Деспертар
источник
Это лучшее решение, если ваше приложение не требует MARS.
Фред Уилсон
7

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

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

int id = adjustmentContext.InformationRequestOrderLinks.Where(item => item.OrderNumber == irOrderLinkVO.OrderNumber && item.InformationRequestId == irOrderLinkVO.InformationRequestId).Max(item => item.Id);

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

List<Entities.InformationRequestOrderLink> links = adjustmentContext.InformationRequestOrderLinks
.Where(item => item.OrderNumber == irOrderLinkVO.OrderNumber && item.InformationRequestId == irOrderLinkVO.InformationRequestId).ToList();

int id = 0;

if (links.Any())
{
  id = links.Max(x => x.Id);
 }
if (id == 0)
{
//do something here
}
Ziggler
источник
5

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

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

return accounts.AsEnumerable()
        .Select((account, index) => new AccountsReport()
        {
          RecordNumber = FormattedRowNumber(account, index + 1),
          CreditRegistryId = account.CreditRegistryId,
          DateLastUpdated = (
                                                from h in context.AccountHistory 
                                                where h.CreditorRegistryId == creditorRegistryId 
                              && h.AccountNo == accountNo 
                                                select h.LastUpdated).Max(),
          AccountNumber = FormattedAccountNumber(account.AccountType, account.AccountNumber)
        })
        .OrderBy(c=>c.FormattedRecordNumber)
        .ThenByDescending(c => c.StateChangeDate);

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

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

Джеймс Александр
источник
Если вы хотите, вы можете выполнить рефакторинг выполняемого запроса, чтобы получить DateLastUpdated непосредственно в вашей проекции Select для запроса отчета об аккаунтах и ​​получить желаемый эффект без ошибок.
Джеймс Александр
Я получаю ту же ошибку после помещения кода функции в основной запрос
DotnetSparrow
2

В дополнение к ответу Ладислава Мрнки :

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

Александр Трощенко
источник
2

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

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

timelmer
источник
2

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

    Dim stores = DataContext.Stores _
        .Where(Function(d) filter.Contains(d.code)) _

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

    Dim stores = DataContext.Stores _
        .Where(Function(d) filter.Contains(d.code)).ToList

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

Public ReadOnly Property Stores As List(Of Store)
    Get
        If _stores Is Nothing Then
            _stores = DataContext.Stores _
                .Where(Function(d) Filters.Contains(d.code)).ToList
        End If
        Return _stores
    End Get
End Property

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

Адам Кокс
источник
1

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

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

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

Майкл Фрейдгейм
источник
1

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

 public async Task<Session> CreateSession()
    {
        var session = new Session();

        _accountSessionDataModel.Sessions.Add(session);
        await _accountSessionDataModel.SaveChangesAsync();
     }

Изменил это на:

 public Session CreateSession()
    {
        var session = new Session();

        _accountSessionDataModel.Sessions.Add(session);
        _accountSessionDataModel.SaveChanges();
     }

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

woutercx
источник
1

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

Эндрю Тейлор
источник
Это была та же проблема с моей стороны. Мне нужен SqlCommand.executeReader (), потому что я получаю ID вставленных строк. Итак: я использовал SqlDataReader.Close (); Sql Command.Dispose (); Спасибо @Andrew Taylor
Fuat
1

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

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

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

usefulBee
источник
1

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

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

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

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

Лучшим подходом является указание EF предварительно загружать все необходимые лениво загруженные данные во время первоначального запроса. Это можно сделать с помощью оператора «Включить»:

using System.Data.Entity;

query = query.Include(a => a.LazyLoadedProperty);

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

Иллидан
источник
Это правильный ответ, потому что я перешел от использования Include к использованию EntityEntry.Collection (). Load (), и мое решение перешло от работы к неработающей. К сожалению, include для универсального элемента не может "ThenInclude" другого универсального элемента, поэтому я все еще пытаюсь заставить EntityEntry.Collection (). Load () работать.
AndrewBenjamin
0

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

 [MethodImpl(MethodImplOptions.Synchronized)]
 public static List<t> MyDBFunction(string parameter1)
  {
  }

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

Пранеш Джанартанан
источник
0

Как примечание ... это также может произойти, когда есть проблема с (внутренним) отображением данных из объектов SQL.

Например...

Я создал, SQL Scalar Functionкоторый случайно возвратил VARCHAR... и затем ... использовал его для генерации столбца в VIEW. Это VIEWбыло правильно отображено в DbContext... так что Линк называл это просто отлично. Тем не менее, Организация ожидает DateTime? и VIEWвозвращал String .

Который БЫСТРО кидает

«Уже есть открытый DataReader, связанный с этой командой, который должен быть закрыт первым»

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

Узник ноль
источник
0

В моем случае я должен был установить MultipleActiveResultSetsзначение Trueв строке подключения.
Затем появилась еще одна ошибка (настоящая) о невозможности одновременного запуска 2 (SQL) команд в одном и том же контексте данных! (EF Core, сначала код).
Поэтому для меня было решение искать любые другие асинхронные команды и переключать их в синхронные , так как у меня был только один DbContext для обеих команд.

Я надеюсь, что это поможет вам

Доктор TJ
источник