Z tym poleceniem jest już otwarty czytnik danych, który należy najpierw zamknąć

640

Mam to zapytanie i pojawia się błąd w tej funkcji:

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();
}

Błąd to:

Z tym poleceniem jest już otwarty czytnik danych, który należy najpierw zamknąć.

Aktualizacja:

dodano ślad stosu:

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
źródło

Odpowiedzi:

1286

Może się to zdarzyć, jeśli wykonasz zapytanie podczas iteracji wyników innego zapytania. W twoim przykładzie nie jest jasne, gdzie to się dzieje, ponieważ przykład nie jest kompletny.

Jedną z rzeczy, które mogą to powodować, jest opóźnione ładowanie uruchamiane podczas iteracji wyników niektórych zapytań.

Można to łatwo rozwiązać, dopuszczając MARS w ciągu połączenia. Dodaj MultipleActiveResultSets=truedo dostawcy część swojego ciągu połączenia (gdzie podano Źródło danych, katalog początkowy itp.).

Ladislav Mrnka
źródło
34
To zadziałało dla mnie. Jeśli chcesz dowiedzieć się więcej o włączaniu wielu aktywnych zestawów wyników (MARS), zobacz msdn.microsoft.com/en-us/library/h32h3abf(v=vs.100).aspx . Zastanów się nad przeczytaniem Wady MARS zbyt stackoverflow.com/questions/374444/...
Diganta Kumar 12.12
3
Biorąc pod uwagę wydajność, można to rozwiązać, włączając System.Data.Entity, a następnie używając instrukcji Include, aby upewnić się, że te dane wtórne zostaną załadowane do pierwotnego zapytania. Jeśli włączysz MARS, wyłączenie go w celu sprawdzenia powtarzających się ładowań danych może przyspieszyć połączenia z przetwarzaniem danych, zmniejszając liczbę podróży w obie strony.
Chris Moschini,
70
Włączenie MARS powinno odbywać się tylko w przypadku bardzo niewielkiej części problemów / przypadków użycia. W większości przypadków dany błąd jest spowodowany BŁĘDNYM KODEM w aplikacji wywołującej. Więcej szczegółów tutaj: devproconnections.com/development/…
Michael K. Campbell
132
Dodanie .ToList () po your.Include (). Where () prawdopodobnie rozwiąże problem.
Serj Sagan
2
Dokonanie globalnej zmiany połączenia SQL dla jednego zapytania jest absurdalne. Prawidłowa odpowiedź powinna brzmieć na liście ToList poniżej. Lokalna poprawka (tj. Wystarczy zmienić zapytanie) dla zlokalizowanego problemu!
bytedev
218

Możesz użyć ToList()metody przed returninstrukcją.

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;
    }
kazem
źródło
9
Miałem ten błąd już tyle razy ... i za każdym razem, gdy zapominam! Odpowiedzią na pytanie jest zawsze użycie ToList ().
Cheesus Toast
1
Czy są w tym jakieś wady? Jeśli masz 100 tys. Wierszy, wątpię, żeby to mogło być dobre.
Martin Dawson,
2
@MartinMazzaDawson, Naprawdę potrzebujesz 100 000 rekordów jednocześnie podczas wykonywania zapytania? myślę, że stosowanie paginacji jest dobrym pomysłem w tej sytuacji
kazem
przepraszam, że podniosłem stary temat, ale napotkałem ten sam błąd podczas tworzenia RepositoryPattern i rozwiązałem go, dodając „.ToList () lub Single () lub Count ()” do każdej metody Repository. Podczas gdy na początku właśnie zwracałem „.AsEnumerable ()”. Teraz moje pytanie brzmi: czy repozytorium zwraca „ToList ()”, czy też jest to coś, co powinno zostać zażądane przez konsumenta końcowego (tj. Logikę usługi / biznesu)
alessalessio
Pracuje dla mnie. Dodanie .ToList rozwiązuje problem z obsługą dziesiętną w JetEntityFrameworkProvider. Total = storeDb.OF_Carts.Where(x => x.CartId == ShoppingCartId).ToList().Sum(t => t.Quantity * t.Item.UnitPrice);
hubert17
39

użyj składni, .ToList()aby przekonwertować obiekt odczytany z bazy danych na listę, aby uniknąć ponownego odczytu. Mam nadzieję, że to zadziała. Dzięki.

Icemark Muturi
źródło
22

Oto działający ciąg połączenia dla kogoś, kto potrzebuje referencji.

  <connectionStrings>
    <add name="IdentityConnection" connectionString="Data Source=(LocalDb)\v11.0;AttachDbFilename=|DataDirectory|\IdentityDb.mdf;Integrated Security=True;MultipleActiveResultSets=true;" providerName="System.Data.SqlClient" />
  </connectionStrings>
Yang Zhang
źródło
15
Włączenie MARS jest obejściem, a NIE rozwiązaniem problemu.
SandRock,
5
Ze strony dokumentacji MARS: „Operacje MARS nie są bezpieczne dla wątków”. Oznacza to, że jeśli problem wynika z wielu wątków uzyskujących dostęp do kontekstu, MARS (prawdopodobnie) nie jest rozwiązaniem.
marsop
20

W moim przypadku użycie Include()rozwiązanego tego błędu i w zależności od sytuacji może być o wiele bardziej wydajne niż wydawanie wielu zapytań, gdy wszystkie mogą być odpytywane jednocześnie za pomocą sprzężenia.

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);
            }
        }
    }
}
Despertar
źródło
Jest to najlepsze rozwiązanie, jeśli Twoja aplikacja nie wymaga MARS.
Fred Wilson,
7

Nie wiem, czy to jest duplikat odpowiedzi, czy nie. Jeśli tak, przepraszam. Chcę tylko powiadomić potrzebujących, jak rozwiązałem problem za pomocą ToList ().

W moim przypadku dostałem ten sam wyjątek dla poniższego zapytania.

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

Rozwiązałem jak poniżej

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
źródło
5

Wygląda na to, że wywołujesz DateLastUpdated z aktywnego zapytania przy użyciu tego samego kontekstu EF, a DateLastUpdate wydaje polecenie do samego magazynu danych. Entity Framework obsługuje tylko jedno aktywne polecenie na kontekst jednocześnie.

Możesz zmienić powyższe dwa zapytania w jedno takie jak to:

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);

Zauważyłem również, że w zapytaniach wywołujesz funkcje takie jak FormattedAccountNumber i FormattedRecordNumber. O ile nie są przechowywane procy lub funkcje zaimportowane z bazy danych do modelu danych encji i poprawnie odwzorowane, będą one również generować wyjątki, ponieważ EF nie będzie wiedział, jak przełożyć te funkcje na instrukcje, które może wysłać do magazynu danych.

Zauważ też, że wywołanie AsEnumerable nie wymusza wykonania zapytania. Do momentu odroczenia wykonania zapytania do czasu wyliczenia. Jeśli chcesz, możesz wymusić wyliczenie za pomocą ToList lub ToArray.

James Alexander
źródło
Jeśli chcesz, możesz refaktoryzować wykonywane zapytanie, aby uzyskać bezpośredni DateLastUpdated w swojej projekcji Wybierz dla zapytania Raport kont i uzyskać pożądany efekt bez błędu.
James Alexander
Ten sam błąd
pojawia się
2

Oprócz odpowiedzi Ladislava Mrnki :

Jeśli publikujesz i zastępujesz kontener na karcie Ustawienia , możesz ustawić MultipleActiveResultSet na True. Możesz znaleźć tę opcję, klikając Zaawansowane ... i będzie ona w grupie Zaawansowane .

Alexander Troshchenko
źródło
2

Dla tych, którzy znajdą to za pośrednictwem Google;
Otrzymywałem ten błąd, ponieważ, jak sugeruje błąd, nie udało mi się zamknąć SqlDataReader przed utworzeniem kolejnego w tym samym SqlCommand, błędnie zakładając, że będzie to śmieci wyrzucane podczas opuszczania metody, w której został utworzony.

Rozwiązałem problem, dzwoniąc sqlDataReader.Close();przed utworzeniem drugiego czytnika.

timelmer
źródło
2

W moim przypadku otworzyłem zapytanie z kontekstu danych, np

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

... a następnie zapytał o to samo ...

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

Dodanie .ToListdo pierwszego rozwiązało mój problem. Myślę, że warto zawinąć to we właściwość taką jak:

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

Gdzie _stores jest zmienną prywatną, a Filters jest również właściwością tylko do odczytu, która czyta z AppSettings.

Adam Cox
źródło
1

Miałem ten sam błąd, gdy próbowałem zaktualizować niektóre rekordy w pętli odczytu. Wypróbowałem najczęściej głosowaną odpowiedź MultipleActiveResultSets=truei stwierdziłem, że jest to obejście problemu, aby uzyskać następny błąd 

Nowa transakcja jest niedozwolona, ​​ponieważ w sesji działają inne wątki

Najlepszym podejściem, które zadziała w przypadku ogromnych zestawów wyników, jest użycie porcji i otwarcie osobnego kontekstu dla każdej porcji, jak opisano w  SqlException from Entity Framework - Nowa transakcja jest niedozwolona, ​​ponieważ w sesji działają inne wątki

Michael Freidgeim
źródło
1

Rozwiązałem ten problem, zmieniając oczekiwanie _accountSessionDataModel.SaveChangesAsync (); to _accountSessionDataModel.SaveChanges (); w mojej klasie Repository.

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

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

Zmieniono na:

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

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

Problem polegał na tym, że zaktualizowałem sesje w interfejsie użytkownika po utworzeniu sesji (w kodzie), ale ponieważ SaveChangesAsync odbywa się asynchronicznie, pobieranie sesji spowodowało ten błąd, ponieważ najwyraźniej operacja SaveChangesAsync nie była jeszcze gotowa.

woutercx
źródło
1

Dla mnie to był mój własny błąd. Próbowałem uruchomić INSERTużywając SqlCommand.executeReader()kiedy powinienem był używany SqlCommand.ExecuteNonQuery(). Został otwarty i nigdy nie został zamknięty, co spowodowało błąd. Uważaj na ten nadzór.

Andrew Taylor
źródło
To był ten sam problem z mojej strony. Potrzebowałem SqlCommand.executeReader (), ponieważ otrzymuję Wstawiony identyfikator wiersza. Więc: użyłem SqlDataReader.Close (); Sql Command.Dispose (); Dzięki @Andrew Taylor
Fuat
1

Wyodrębniono to ze scenariusza ze świata rzeczywistego:

  • Kod działa dobrze w środowisku Stage, gdy w parametrze połączenia ustawiono MultipleActiveResultSets
  • Kod opublikowany w środowisku produkcyjnym bez MultipleActiveResultSets = true
  • Tak wiele stron / połączeń działa, gdy jedna zawiedzie
  • Przyglądając się bliżej wywołaniu, jest niepotrzebne wywołanie do bazy danych i należy je usunąć
  • Ustaw MultipleActiveResultSets = true w Production i publikuj oczyszczony kod, wszystko działa dobrze i wydajnie

Podsumowując, nie zapominając o MultipleActiveResultSets, kod mógł działać przez długi czas przed wykryciem nadmiarowego wywołania db, które może być bardzo kosztowne, i sugeruję, aby nie polegać w pełni na ustawianiu atrybutu MultipleActiveResultSets, ale także dowiedzieć się, dlaczego kod go potrzebuje gdzie się nie udało .

przydatne
źródło
1

Najprawdopodobniej ten problem występuje z powodu funkcji „leniwego ładowania” Entity Framework. Zwykle, chyba że jest to wyraźnie wymagane podczas wstępnego pobierania, wszystkie połączone dane (wszystko, co przechowywane w innych tabelach bazy danych) są pobierane tylko wtedy, gdy jest to wymagane. W wielu przypadkach jest to dobra rzecz, ponieważ zapobiega pobieraniu niepotrzebnych danych, a tym samym poprawia wydajność zapytań (brak połączeń) i oszczędza przepustowość.

W sytuacji opisanej w pytaniu wykonywane jest wstępne pobieranie, a podczas fazy „wyboru” żądane jest brakujące opóźnione ładowanie danych, wysyłane są dodatkowe zapytania, a następnie EF narzeka na „otwarty DataReader”.

Obejście zaproponowane w zaakceptowanej odpowiedzi pozwoli na wykonanie tych zapytań i rzeczywiście całe żądanie zakończy się powodzeniem.

Jeśli jednak sprawdzisz żądania wysłane do bazy danych, zauważysz wiele żądań - dodatkowe żądanie dla każdego brakującego (leniwie załadowanego) danych. To może być zabójca wydajności.

Lepszym rozwiązaniem jest poinformowanie EF o wstępnym załadowaniu wszystkich potrzebnych leniwie załadowanych danych podczas pierwszego zapytania. Można to zrobić za pomocą instrukcji „Uwzględnij”:

using System.Data.Entity;

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

W ten sposób wszystkie potrzebne sprzężenia zostaną wykonane, a wszystkie potrzebne dane zostaną zwrócone jako pojedyncze zapytanie. Problem opisany w pytaniu zostanie rozwiązany.

Illidan
źródło
To poprawna odpowiedź, ponieważ przeszedłem z użycia Include na EntityEntry.Collection (). Load (), a moje rozwiązanie zmieniło się z pracy na zepsute. Niestety, dołączenie do generycznego nie może „ThenInclude” innego generycznego, więc wciąż próbuję uruchomić EntityEntry.Collection (). Load ().
AndrewBenjamin
0

Korzystam z usługi sieci Web w moim narzędziu, z którego usługi pobierają procedurę składowaną. podczas gdy większa liczba narzędzi klienckich pobiera usługę internetową, pojawia się ten problem. Naprawiłem, określając atrybut synchronizowany dla tych funkcji, które pobierają procedurę przechowywaną. teraz działa dobrze, błąd nigdy nie pojawił się w moim narzędziu.

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

Ten atrybut umożliwia przetwarzanie jednego żądania na raz. więc to rozwiązuje problem.

Pranesh Janarthanan
źródło
0

Na marginesie ... może się to również zdarzyć, gdy występuje problem z (wewnętrznym) odwzorowaniem danych z obiektów SQL.

Na przykład...

Stworzyłem SQL Scalar Functionże przypadkowo zwracana VARCHAR... a potem ... używali go do wygenerowania kolumny w VIEW. VIEWZostał prawidłowo odwzorowane w DbContext... więc LINQ został nazywając go po prostu w porządku. Jednak jednostka oczekiwała DateTime? i VIEWzwracał String .

Który ODDLY rzuca ...

„Z tym poleceniem jest już otwarty czytnik danych, który należy najpierw zamknąć”

Ciężko było to rozgryźć ... ale po poprawieniu parametrów zwrotu ... wszystko było dobrze

Więzień ZERO
źródło
0

W moim przypadku musiałem ustawić MultipleActiveResultSetsto Truew ciągu połączenia.
Potem pojawił się kolejny błąd (prawdziwy) dotyczący niemożności uruchomienia 2 poleceń SQL w tym samym czasie w tym samym kontekście danych! (EF Core, najpierw kod)
. Rozwiązaniem było dla mnie poszukiwanie dowolnego wykonania asynchronicznego polecenia i przełączenie ich w tryb synchroniczny , ponieważ miałem tylko jeden DbContext dla obu poleceń.

Mam nadzieję, że Ci to pomoże

Dr TJ
źródło