Diesem Befehl ist bereits ein offener DataReader zugeordnet, der zuerst geschlossen werden muss

638

Ich habe diese Abfrage und erhalte den Fehler in dieser Funktion:

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

Fehler ist:

Diesem Befehl ist bereits ein offener DataReader zugeordnet, der zuerst geschlossen werden muss.

Aktualisieren:

Stack-Trace hinzugefügt:

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
quelle

Antworten:

1282

Dies kann passieren, wenn Sie eine Abfrage ausführen, während Sie die Ergebnisse einer anderen Abfrage durchlaufen. Aus Ihrem Beispiel geht nicht hervor, wo dies geschieht, da das Beispiel nicht vollständig ist.

Eine Sache, die dies verursachen kann, ist das verzögerte Laden, das ausgelöst wird, wenn die Ergebnisse einer Abfrage durchlaufen werden.

Dies kann leicht gelöst werden, indem MARS in Ihrer Verbindungszeichenfolge zugelassen wird. Fügen Sie MultipleActiveResultSets=truedem Provider-Teil Ihrer Verbindungszeichenfolge hinzu (wo Datenquelle, Erstkatalog usw. angegeben sind).

Ladislav Mrnka
quelle
34
Das hat bei mir funktioniert. Weitere Informationen zum Aktivieren mehrerer aktiver Ergebnismengen (MARS) finden Sie unter msdn.microsoft.com/en-us/library/h32h3abf(v=vs.100).aspx . Lesen Sie auch die Nachteile von MARS Stackoverflow.com/questions/374444/…
Diganta Kumar
3
Unter Berücksichtigung der Leistung können Sie dies auch beheben, indem Sie System.Data.Entity einschließen und dann Include-Anweisungen verwenden, um sicherzustellen, dass diese sekundären Daten in die ursprüngliche Abfrage geladen werden. Wenn Sie MARS aktivieren, können Sie durch Deaktivieren von MARS zur Überprüfung dieser wiederholten Datenladevorgänge Ihre Datenverarbeitungsanrufe beschleunigen, indem Sie Roundtrips reduzieren.
Chris Moschini
70
Das Aktivieren von MARS sollte nur für eine sehr kleine Teilmenge von Problemen / Anwendungsfällen erfolgen. In den meisten Fällen wird der betreffende Fehler durch BAD CODE in der aufrufenden Anwendung verursacht. Weitere Details hier: devproconnections.com/development/…
Michael K. Campbell
132
Das Hinzufügen von .ToList () nach your.Include (). Where () wird das Problem wahrscheinlich lösen.
Serj Sagan
2
Es ist lächerlich, eine globale SQL-Verbindung für eine Abfrage zu ändern. Die richtige Antwort sollte die ToList unten sein. Ein lokaler Fix (dh nur die Abfrage ändern) für ein lokalisiertes Problem!
Bytedev
217

Sie können die ToList()Methode vor der returnAnweisung verwenden.

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
quelle
9
Ich habe diesen Fehler jetzt so oft gehabt ... und jedes Mal, wenn ich es vergesse! Die Antwort auf die Frage lautet immer ToList ().
Cheesus Toast
1
Gibt es irgendwelche Nachteile? Wenn Sie 100.000 Zeilen haben, bezweifle ich, dass dies gut sein kann.
Martin Dawson
2
@ MartinMazzaDawson, Sie brauchen wirklich 100.000 Datensätze auf einmal Abfrageausführung? Ich denke, dass die Verwendung von Paginierung eine gute Idee für diese Situation ist
Kazem
Es tut mir leid, ein altes Thema anzusprechen, aber ich bin beim Entwickeln eines RepositoryPattern auf denselben Fehler gestoßen und habe ihn durch Hinzufügen von ".ToList () oder Single () oder Count ()" zu jeder Methode des Repositorys behoben. Während ich am Anfang gerade ".AsEnumerable ()" zurückgab. Jetzt ist meine Frage: Sollte das Repository die "ToList ()" zurückgeben, oder ist dies etwas, das an den Endverbraucher verlangt werden sollte (dh: die Service- / Geschäftslogik)
alessalessio
Funktioniert bei mir. Durch Hinzufügen von .ToList wird das Problem der Dezimalunterstützung in JetEntityFrameworkProvider behoben. Total = storeDb.OF_Carts.Where(x => x.CartId == ShoppingCartId).ToList().Sum(t => t.Quantity * t.Item.UnitPrice);
Hubert17
39

Verwenden Sie die Syntax .ToList(), um das von db gelesene Objekt in eine Liste zu konvertieren, damit es nicht erneut gelesen wird. Ich hoffe, dies würde funktionieren. Vielen Dank.

Icemark Muturi
quelle
22

Hier ist eine funktionierende Verbindungszeichenfolge für jemanden, der eine Referenz benötigt.

  <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
quelle
15
Das Aktivieren von MARS ist eine Problemumgehung und KEINE Lösung für das Problem.
SandRock
5
Auf der MARS-Dokumentationsseite: "MARS-Vorgänge sind nicht threadsicher." Das heißt, wenn das Problem durch mehrere Threads entsteht, die auf den Kontext zugreifen, ist MARS (wahrscheinlich) nicht die Lösung.
Marsop
20

In meinem Fall kann die Verwendung Include()dieses Fehlers und je nach Situation viel effizienter sein als die Ausgabe mehrerer Abfragen, wenn alle gleichzeitig mit einem Join abgefragt werden können.

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
quelle
Dies ist die beste Lösung, wenn für Ihre Anwendung ansonsten kein MARS erforderlich ist.
Fred Wilson
7

Ich weiß nicht, ob dies eine doppelte Antwort ist oder nicht. Wenn es so ist, tut es mir leid. Ich möchte nur die Bedürftigen wissen lassen, wie ich mein Problem mit ToList () gelöst habe.

In meinem Fall habe ich die gleiche Ausnahme für die folgende Abfrage erhalten.

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

Ich habe wie unten gelöst

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
quelle
5

Es scheint, dass Sie DateLastUpdated aus einer aktiven Abfrage heraus mit demselben EF-Kontext aufrufen und DateLastUpdate einen Befehl an den Datenspeicher selbst ausgibt. Entity Framework unterstützt jeweils nur einen aktiven Befehl pro Kontext.

Sie können Ihre beiden oben genannten Abfragen wie folgt in eine umgestalten:

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

Mir ist auch aufgefallen, dass Sie in den Abfragen Funktionen wie FormattedAccountNumber und FormattedRecordNumber aufrufen. Sofern es sich nicht um gespeicherte Prozesse oder Funktionen handelt, die Sie aus Ihrer Datenbank in das Entitätsdatenmodell importiert und korrekt zugeordnet haben, werden auch Ausnahmen ausgelöst, da EF nicht weiß, wie diese Funktionen in Anweisungen übersetzt werden können, die an den Datenspeicher gesendet werden können.

Beachten Sie außerdem, dass beim Aufrufen von AsEnumerable die Ausführung der Abfrage nicht erzwungen wird. Bis die Abfrageausführung bis zur Aufzählung verschoben wird. Sie können die Aufzählung mit ToList oder ToArray erzwingen, wenn Sie dies wünschen.

James Alexander
quelle
Wenn Sie möchten, können Sie die von Ihnen ausgeführte Abfrage umgestalten, um die DateLastUpdated-Direktive in Ihre Select-Projektion für die Abfrage "Kontenbericht" zu übernehmen und den gewünschten Effekt ohne Fehler zu erzielen.
James Alexander
Ich
erhalte
2

Zusätzlich zu Ladislav Mrnkas Antwort:

Wenn Sie Container auf der Registerkarte Einstellungen veröffentlichen und überschreiben , können Sie MultipleActiveResultSet auf True setzen. Sie finden diese Option, indem Sie auf Erweitert ... klicken. Sie befindet sich in der Gruppe Erweitert .

Alexander Troshchenko
quelle
2

Für diejenigen, die dies über Google finden;
Ich habe diesen Fehler erhalten, weil ich, wie durch den Fehler vorgeschlagen, einen SqlDataReader nicht schließen konnte, bevor ich einen anderen auf demselben SqlCommand erstellt habe, und fälschlicherweise angenommen habe, dass beim Verlassen der Methode, in der er erstellt wurde, Müll gesammelt wird.

Ich habe das Problem durch einen Anruf gelöst, sqlDataReader.Close();bevor ich den zweiten Leser erstellt habe.

Timelmer
quelle
2

In meinem Fall hatte ich eine Abfrage aus dem Datenkontext wie geöffnet

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

... und anschließend gleich abgefragt ...

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

Das Hinzufügen des .ToListzum ersten hat mein Problem behoben. Ich denke, es ist sinnvoll, dies in eine Eigenschaft wie:

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

Wobei _stores eine private Variable ist und Filter auch eine schreibgeschützte Eigenschaft ist, die aus AppSettings liest.

Adam Cox
quelle
1

Ich hatte den gleichen Fehler, als ich versuchte, einige Datensätze innerhalb der Leseschleife zu aktualisieren. Ich habe die am häufigsten gewählte Antwort ausprobiert MultipleActiveResultSets=trueund festgestellt, dass es nur eine Problemumgehung ist, um den nächsten Fehler zu erhalten 

Neue Transaktionen sind nicht zulässig, da in der Sitzung andere Threads ausgeführt werden

Der beste Ansatz, der für große ResultSets funktioniert, besteht darin, Chunks zu verwenden und für jeden Chunk einen eigenen Kontext zu öffnen, wie in SqlException from Entity Framework beschrieben.  Eine neue Transaktion ist nicht zulässig, da in der Sitzung andere Threads ausgeführt werden

Michael Freidgeim
quelle
1

Ich habe dieses Problem gelöst, indem ich wait _accountSessionDataModel.SaveChangesAsync () geändert habe. to _accountSessionDataModel.SaveChanges (); in meiner Repository-Klasse.

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

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

Geändert zu:

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

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

Das Problem war, dass ich die Sitzungen im Frontend nach dem Erstellen einer Sitzung (im Code) aktualisiert habe. Da SaveChangesAsync jedoch asynchron erfolgt, verursachte das Abrufen der Sitzungen diesen Fehler, da der SaveChangesAsync-Vorgang anscheinend noch nicht bereit war.

woutercx
quelle
1

Für mich war es mein eigener Fehler. Ich habe versucht, ein INSERTusing auszuführen , SqlCommand.executeReader()wenn ich hätte verwenden sollen SqlCommand.ExecuteNonQuery(). Es wurde geöffnet und nie geschlossen, was den Fehler verursachte. Achten Sie auf dieses Versehen.

Andrew Taylor
quelle
Es war das gleiche Problem von meiner Seite. Ich brauchte SqlCommand.executeReader (), weil ich die ID eingefügter Zeilen erhalte. Also: Ich habe SqlDataReader.Close () verwendet; SQL Command.Dispose (); Danke @ Andrew Taylor
Fuat
1

Dies wird aus einem realen Szenario extrahiert:

  • Code funktioniert gut in einer Stage-Umgebung, in der MultipleActiveResultSets in der Verbindungszeichenfolge festgelegt ist
  • In der Produktionsumgebung veröffentlichter Code ohne MultipleActiveResultSets = true
  • So viele Seiten / Aufrufe funktionieren, während eine einzelne fehlschlägt
  • Bei näherer Betrachtung des Anrufs wird die Datenbank unnötig angerufen und muss entfernt werden
  • Setzen Sie MultipleActiveResultSets = true in der Produktion und veröffentlichen Sie bereinigten Code. Alles funktioniert gut und effizient

Ohne MultipleActiveResultSets zu vergessen, wurde der Code möglicherweise lange ausgeführt, bevor ein redundanter Datenbankaufruf entdeckt wurde, der sehr kostspielig sein kann. Ich empfehle, nicht vollständig vom Festlegen des MultipleActiveResultSets-Attributs abhängig zu sein, sondern auch herauszufinden, warum der Code ihn benötigt wo es fehlgeschlagen ist .

nützlichBee
quelle
1

Dieses Problem tritt höchstwahrscheinlich aufgrund der Funktion "Lazy Loading" von Entity Framework auf. Normalerweise werden alle verknüpften Daten (alles, was in anderen Datenbanktabellen gespeichert ist) nur bei Bedarf abgerufen, sofern dies beim ersten Abruf nicht ausdrücklich erforderlich ist. In vielen Fällen ist dies eine gute Sache, da es das Abrufen unnötiger Daten verhindert und somit die Abfrageleistung verbessert (keine Verknüpfungen) und Bandbreite spart.

In der in der Frage beschriebenen Situation wird ein anfänglicher Abruf durchgeführt, und während der "Auswahl" -Phase werden fehlende Daten zum verzögerten Laden angefordert, zusätzliche Abfragen werden ausgegeben, und dann beschwert sich EF über "open DataReader".

Die in der akzeptierten Antwort vorgeschlagene Problemumgehung ermöglicht die Ausführung dieser Abfragen, und tatsächlich ist die gesamte Anforderung erfolgreich.

Wenn Sie jedoch an die Datenbank gesendete Anforderungen untersuchen, werden Sie mehrere Anforderungen bemerken - zusätzliche Anforderung für jede fehlende (verzögert geladene) Daten. Dies könnte ein Leistungskiller sein.

Ein besserer Ansatz besteht darin, EF anzuweisen, alle benötigten verzögert geladenen Daten während der ersten Abfrage vorzuladen. Dies kann mit der Anweisung "Include" erfolgen:

using System.Data.Entity;

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

Auf diese Weise werden alle erforderlichen Verknüpfungen ausgeführt und alle erforderlichen Daten als einzelne Abfrage zurückgegeben. Das in der Frage beschriebene Problem wird gelöst.

Illidan
quelle
Dies ist eine gültige Antwort, da ich von Include zu EntityEntry.Collection (). Load () übergegangen bin und meine Lösung von funktionierend zu fehlerhaft gewechselt ist. Leider kann include für ein Generikum kein anderes Generikum "ThenInclude", daher versuche ich immer noch, EntityEntry.Collection (). Load () zum Laufen zu bringen.
AndrewBenjamin
0

Ich verwende den Webdienst in meinem Tool, wobei dieser Dienst die gespeicherte Prozedur abruft. Während mehr Client-Tools den Webdienst abrufen, tritt dieses Problem auf. Ich habe durch Angabe des Synchronized-Attributs für diese Funktion die gespeicherte Prozedur abgerufen. Jetzt funktioniert es einwandfrei, der Fehler ist in meinem Tool nie aufgetreten.

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

Mit diesem Attribut kann jeweils eine Anforderung verarbeitet werden. Dies löst das Problem.

Pranesh Janarthanan
quelle
0

Als Randnotiz ... kann dies auch passieren, wenn ein Problem mit der (internen) Datenzuordnung von SQL-Objekten vorliegt.

Zum Beispiel...

Ich habe eine erstellt SQL Scalar Function, die versehentlich eine VARCHAR... zurückgegeben hat, und dann ... eine Spalte in a generiert VIEW. Das VIEWwar korrekt in der DbContext... abgebildet, also nannte Linq es ganz gut. Die Entität erwartet jedoch DateTime? und der VIEWgab String zurück .

Was ODDLY wirft ...

"Diesem Befehl ist bereits ein offener DataReader zugeordnet, der zuerst geschlossen werden muss."

Es war schwer herauszufinden ... aber nachdem ich die Rückgabeparameter korrigiert hatte ... war alles in Ordnung

Gefangener NULL
quelle
0

In meinem Fall hatte ich das einstellen , MultipleActiveResultSetsum Truein der Verbindungszeichenfolge.
Dann erschien ein weiterer Fehler (der echte), dass nicht zwei (SQL) Befehle gleichzeitig über denselben Datenkontext ausgeführt werden konnten! (EF Core, Code zuerst)
Die Lösung für mich bestand also darin, nach einer anderen asynchronen Befehlsausführung zu suchen und diese auf synchron zu schalten , da ich nur einen DbContext für beide Befehle hatte.

Ich hoffe es hilft dir

Dr. TJ
quelle