З цією командою вже є відкритий DataReader, який потрібно спочатку закрити


640

У мене є цей запит, і я отримую помилку в цій функції:

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

Відповіді:


1287

Це може статися, якщо ви виконаєте запит під час ітерації над результатами іншого запиту. З вашого прикладу незрозуміло, де це відбувається, тому що приклад не є повним.

Одне, що може спричинити це, - це ліниве завантаження, яке спрацьовує під час повторення результатів запиту.

Це можна легко вирішити, включивши 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, а потім за допомогою операторів Включити, щоб забезпечити завантаження цих вторинних даних у вихідний запит. Якщо ви ввімкнули MARS, вимкнення його для перевірки цих повторних навантажень даних може допомогти пришвидшити обробку дзвінків за рахунок зменшення зворотних поїздок.
Кріс Москіні

70
Увімкнення MARS слід проводити лише для дуже невеликого набору проблем / випадків використання. У більшості випадків, про яку йдеться, помилка викликається BAD CODE у програмі, що викликає. Детальніше тут: devproconnections.com/development/…
Майкл К. Кемпбелл

132
Додавання .ToList () після your.Include (). Де (), ймовірно, вирішить проблему.
Серж Саган

2
Зробити глобальне підключення SQL широке зміна для одного запиту смішно. Правильна відповідь має бути списком ToList нижче. Місцеве виправлення (тобто просто змінити запит) для локалізованої проблеми!
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 ().
Чіз-тост

1
Чи є в цьому недоліки? Якщо у вас є 100k рядків, я сумніваюся, це може бути добре.
Мартін Доусон

2
@MartinMazzaDawson, вам дійсно потрібно відразу 100K записів виконання запиту ?? Я думаю, що використання пагинації є хорошою ідеєю для цієї ситуації
kazem

Вибачте, що підняли стару тему, але я зіткнувся з тією ж помилкою під час розробки RepositoryPattern, і я вирішив її, додавши ".ToList () або Single () або Count ()" до кожного методу Репозиторію. Тоді як на початку я тільки повертався ".AsEnumerable ()". Тепер моє запитання: чи потрібно сховище, яке повертає "ToList ()", чи це щось, що потрібно вимагати від кінцевого споживача (тобто: логіка послуги / бізнесу)
alessalessio

Працює для мене. Додавання .ToList вирішує проблему підтримки десяткової підтримки в JetEntityFrameworkProvider. Total = storeDb.OF_Carts.Where(x => x.CartId == ShoppingCartId).ToList().Sum(t => t.Quantity * t.Item.UnitPrice);
hubert17

39

використовуйте синтаксис .ToList()для перетворення прочитаного об'єкта з db у список, щоб уникнути його повторного читання. Дякую.


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 (ймовірно) не є рішенням.
марсоп

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
}

5

Схоже, ви викликаєте DateLastUpdate з активного запиту, використовуючи той самий контекст 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, якщо цього захочете.


Якщо ви хочете, ви можете перефактурувати запит, який ви виконуєте, щоб отримати пряму дату DateLastUpdate у вашу Вибір проекції для запиту звіту про рахунки та отримати бажаний ефект без помилок.
Джеймс Олександр

Я отримую таку ж помилку після введення коду функції всередині основного запиту
DotnetSparrow

2

Окрім Ладіслава Мрнка :

Якщо ви публікуєте та перезавантажуєте контейнер на вкладці Налаштування , ви можете встановити для параметра MultipleActiveResultSet значення True. Ви можете знайти цю опцію, натиснувши Додатково ..., і це буде під групою Додатково .


2

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

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


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 - це приватна змінна, а Фільтри - також властивість лише для читання, яка читається з AppSettings.


1

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

Нова транзакція заборонена, оскільки в сеансі працюють інші потоки

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


1

Я вирішив цю проблему, змінивши wait_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();
     }

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


1

Ну для мене це була моя власна помилка. Я намагався запустити INSERTкористування, SqlCommand.executeReader()коли мені слід було користуватися SqlCommand.ExecuteNonQuery(). Він був відкритий і ніколи не закритий, викликаючи помилку. Слідкуйте за цим наглядом.


Це було те саме з мого боку. Мені потрібен SqlCommand.executeReader (), тому що я отримую Вставлений ідентифікатор рядків. Отже: я використав SqlDataReader.Close (); Sql Command.Dispose (); Дякуємо @Andrew Taylor
Fuat

1

Це витягнуто з реального сценарію:

  • Код добре працює в середовищі Stage з MultipleActiveResultSets встановлений у рядку з'єднання
  • Код, опублікований у виробничому середовищі без MultipleActiveResultSets = true
  • Так багато сторінок / дзвінків працюють, коли жодна з них не працює
  • Придивившись ближче до виклику, у db робиться непотрібний дзвінок і його потрібно видалити
  • Встановіть MultipleActiveResultSets = вірно у виробництві та опублікуйте очищений код, все працює добре та ефективно

На закінчення, не забуваючи про MultipleActiveResultSets, код, можливо, працював би тривалий час, перш ніж виявити надлишковий db-дзвінок, який може бути дуже дорогим, і я пропоную не повністю залежати від встановлення атрибута MultipleActiveResultSets, а також з’ясувати, чому коду він потрібен де не вдалося .


1

Швидше за все, ця проблема трапляється через особливості "ледачого завантаження" Entity Framework. Зазвичай, якщо явно не вимагається під час початкового вилучення, всі об'єднані дані (все, що зберігається в інших таблицях бази даних) отримують лише за потреби. У багатьох випадках це є хорошою справою, оскільки це заважає отримувати непотрібні дані і тим самим покращує продуктивність запитів (не приєднується) та економить пропускну здатність.

У ситуації, описаній у запитанні, виконується початкове завантаження, і під час фази "вибору" відсутні дані про ледачі завантаження, видаються додаткові запити, а потім EF скаржиться на "відкритий DataReader".

Запропонований у прийнятій відповіді спосіб вирішення дасть змогу виконати ці запити, і справді весь запит матиме успіх.

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

Кращий підхід - сказати EF, щоб попередньо завантажити всі необхідні ледачі завантажені дані під час початкового запиту. Це можна зробити за допомогою оператора "Включити":

using System.Data.Entity;

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

Таким чином, всі необхідні об’єднання будуть виконані, а всі необхідні дані будуть повернуті як єдиний запит. Питання, описане в питанні, буде вирішено.


Це правильна відповідь, тому що я перейшов від використання включати до використання EntityEntry.Collection (). Load (), і моє рішення перейшло від роботи до зламаної. На жаль, включення для загального не може "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... так Linq кликала його просто відмінно. Однак суб'єкт господарювання очікував DateTime? і струнаVIEW поверталася .

Який ODDLY кидає ...

"Вже є відкритий DataReader, пов'язаний з цією командою, який потрібно спочатку закрити."

Важко було це зрозуміти ... але після того, як я виправив параметри повернення ... все було добре


0

У моєму випадку, я повинен був встановити , MultipleActiveResultSetsщоб Trueв рядку підключення.
Потім з'явилася ще одна помилка (справжня) про те, що не можна одночасно запускати 2 команди (SQL) над тим самим контекстом даних! (EF Core, Код спочатку)
Тому для мене рішення було шукати будь-яке інше виконання асинхронних команд і перетворити їх на синхронні , так як у мене був лише один DbContext для обох команд.

Сподіваюся, це допоможе тобі

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.