Esiste già un DataReader aperto associato a questo comando che deve essere chiuso per primo


641

Ho questa domanda e ottengo l'errore in questa funzione:

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

L'errore è:

Esiste già un DataReader aperto associato a questo comando che deve essere chiuso per primo.

Aggiornare:

traccia stack aggiunta:

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

Risposte:


1288

Ciò può accadere se si esegue una query mentre si ripetono i risultati di un'altra query. Dal tuo esempio non è chiaro dove ciò accada perché l'esempio non è completo.

Una cosa che può causare questo è il caricamento lento attivato durante l'iterazione sui risultati di alcune query.

Ciò può essere facilmente risolto consentendo MARS nella stringa di connessione. Aggiungi MultipleActiveResultSets=trueal provider parte della stringa di connessione (dove sono specificati Origine dati, Catalogo iniziale, ecc.).


34
Questo ha funzionato per me. Per ulteriori informazioni sull'abilitazione di set di risultati attivi multipli (MARS), consultare msdn.microsoft.com/en-us/library/h32h3abf(v=vs.100).aspx . Considerare la lettura su svantaggi di MARS troppo stackoverflow.com/questions/374444/...
Diganta Kumar

3
Tenendo conto delle prestazioni, è anche possibile risolverlo includendo System.Data.Entity e quindi utilizzando le istruzioni Include per assicurarsi che questi dati secondari vengano caricati nella query originale. Se si abilita MARS, disattivarlo per verificare la presenza di ripetuti caricamenti di dati può aiutare ad accelerare le chiamate di elaborazione dei dati riducendo i round trip.
Chris Moschini,

70
L'abilitazione di MARS dovrebbe essere eseguita solo per un piccolo sottoinsieme di problemi / casi d'uso. Nella maggior parte dei casi, l'errore in questione è causato da BAD CODE all'interno dell'applicazione chiamante. Maggiori dettagli qui: devproconnections.com/development/…
Michael K. Campbell,

132
Aggiunta di .ToList () dopo your.Include (). Where () probabilmente risolverà il problema.
Serj Sagan,

2
Per rendere una connessione globale SQL ampia modifica per una query è ridicolo. La risposta corretta dovrebbe essere quella ToList qui sotto. Una correzione locale (cioè basta cambiare la query) per un problema localizzato!
bytedev,

218

È possibile utilizzare il ToList()metodo prima returndell'istruzione.

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
Ho avuto questo errore così tante volte ora ... e ogni volta che dimentico! La risposta alla domanda è sempre usare ToList ().
Cheesus Toast,

1
Ci sono degli svantaggi in questo? Se hai 100k righe dubito che questo possa essere buono.
Martin Dawson,

2
@MartinMazzaDawson, hai davvero bisogno di 100K record in una volta sola esecuzione della query ?? penso che usare l'impaginazione sia una buona idea per questa situazione
kazem,

mi dispiace sollevare un vecchio argomento ma mi sono imbattuto nello stesso errore durante lo sviluppo di un RepositoryPattern e l'ho risolto aggiungendo ".ToList () o Single () o Count ()" a ogni metodo del repository. Mentre all'inizio stavo solo tornando ".AsEnumerable ()". Ora la mia domanda è: dovrebbe il repository restituire il "ToList ()", o è qualcosa che deve essere richiesto al consumatore finale (cioè: il servizio / la logica aziendale)
alessalessio

Per me va bene. L'aggiunta di .ToList risolve il problema del supporto decimale in JetEntityFrameworkProvider. Total = storeDb.OF_Carts.Where(x => x.CartId == ShoppingCartId).ToList().Sum(t => t.Quantity * t.Item.UnitPrice);
hubert17,

39

usa la sintassi .ToList()per convertire l'oggetto letto da db in elenco per evitare di essere riletto di nuovo. Spero che funzionerebbe per questo. Grazie.


22

Ecco una stringa di connessione funzionante per qualcuno che ha bisogno di riferimenti.

  <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
Abilitare MARS è una soluzione alternativa, NON una soluzione al problema.
SandRock

5
Dalla pagina della documentazione MARS: "Le operazioni MARS non sono thread-safe". Ciò significa che se il problema deriva da più thread che accedono al contesto, MARS non è (probabilmente) la soluzione.
Marsop,

20

Nel mio caso, l'utilizzo di Include()questo errore risolto e, a seconda della situazione, può essere molto più efficiente rispetto all'emissione di più query quando è possibile eseguire tutte le query contemporaneamente con un join.

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

Questa è la soluzione migliore se la tua applicazione non richiede MARS.
Fred Wilson,

7

Non so se questa è una risposta duplicata o meno. Se lo è, mi dispiace. Voglio solo far sapere ai bisognosi come ho risolto il mio problema usando ToList ().

Nel mio caso ho ottenuto la stessa eccezione per la query di seguito.

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

Ho risolto come di seguito

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

Sembra che stai chiamando DateLastUpdated da una query attiva utilizzando lo stesso contesto EF e DateLastUpdate invia un comando all'archivio dati stesso. Entity Framework supporta solo un comando attivo per contesto alla volta.

Puoi trasformare le tue due query precedenti in una in questo modo:

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

Ho anche notato che stai chiamando funzioni come FormattedAccountNumber e FormattedRecordNumber nelle query. A meno che questi non siano processi o funzioni memorizzati che hai importato dal tuo database nel modello di dati dell'entità e mappati correttamente, questi genereranno anche eccezioni poiché EF non saprà come tradurre tali funzioni in istruzioni che può inviare all'archivio dati.

Inoltre, la chiamata a AsEnumerable non impone l'esecuzione della query. Fino a quando l'esecuzione della query viene rinviata fino all'enumerazione. Puoi forzare l'enumerazione con ToList o ToArray se lo desideri.


Se lo desideri, puoi eseguire il refactoring della query che stai eseguendo per ottenere il DateLastUpdated direttamente nella tua proiezione Seleziona per la query Rapporto clienti e ottenere l'effetto desiderato senza l'errore.
James Alexander,

Ricevo lo stesso errore dopo aver
inserito

2

Oltre a Ladislav Mrnka's risposta di :

Se si sta pubblicando e sovrascrivendo il contenitore nella scheda Impostazioni , è possibile impostare MultipleActiveResultSet su True. Puoi trovare questa opzione facendo clic su Avanzate ... e sarà nel gruppo Avanzate .


2

Per chi lo trova tramite Google;
Stavo ottenendo questo errore perché, come suggerito dall'errore, non sono riuscito a chiudere un SqlDataReader prima di crearne un altro sullo stesso SqlCommand, presumendo erroneamente che sarebbe stato spazzato via quando si lasciava il metodo in cui era stato creato.

Ho risolto il problema chiamando sqlDataReader.Close();prima di creare il secondo lettore.


2

Nel mio caso, avevo aperto una query dal contesto dei dati, come

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

... e successivamente interrogato lo stesso ...

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

L'aggiunta .ToListal primo ha risolto il mio problema. Penso che abbia senso avvolgerlo in una proprietà come:

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

Dove _stores è una variabile privata e Filters è anche una proprietà di sola lettura che legge da AppSettings.


1

Ho avuto lo stesso errore, quando ho provato ad aggiornare alcuni record all'interno del ciclo di lettura. Ho provato la risposta più votata MultipleActiveResultSets=truee ho scoperto che è solo una soluzione alternativa per ottenere il prossimo errore 

La nuova transazione non è consentita perché nella sessione sono in esecuzione altri thread

L'approccio migliore, che funzionerà con enormi ResultSet è di usare blocchi e aprire un contesto separato per ogni blocco come descritto in  SqlException da Entity Framework - La nuova transazione non è consentita perché ci sono altri thread in esecuzione nella sessione


1

Ho risolto questo problema cambiando await _accountSessionDataModel.SaveChangesAsync (); a _accountSessionDataModel.SaveChanges (); nella mia classe Repository.

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

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

Modificato in:

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

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

Il problema era che ho aggiornato le sessioni nel frontend dopo aver creato una sessione (nel codice), ma poiché SaveChangesAsync si verifica in modo asincrono, il recupero delle sessioni ha causato questo errore perché apparentemente l'operazione SaveChangesAsync non era ancora pronta.


1

Beh, per me era il mio bug. Stavo cercando di eseguire un INSERTutilizzo SqlCommand.executeReader()quando avrei dovuto utilizzare SqlCommand.ExecuteNonQuery(). È stato aperto e mai chiuso, causando l'errore. Fai attenzione a questa svista.


Era lo stesso problema da parte mia. Avevo bisogno di SqlCommand.executeReader () perché sto ottenendo l'ID delle righe inserite. Quindi: ho usato SqlDataReader.Close (); Sql Command.Dispose (); Grazie @Andrew Taylor
Fuat il

1

Questo è estratto da uno scenario del mondo reale:

  • Il codice funziona bene in un ambiente Stage con MultipleActiveResultSets è impostato nella stringa di connessione
  • Codice pubblicato nell'ambiente di produzione senza MultipleActiveResultSets = true
  • Tante pagine / chiamate funzionano mentre una singola non funziona
  • Guardando più da vicino alla chiamata, c'è una chiamata non necessaria fatta al db e deve essere rimossa
  • Imposta MultipleActiveResultSets = true in Production e pubblica il codice pulito, tutto funziona bene e, in modo efficiente

In conclusione, senza dimenticare di MultipleActiveResultSets, il codice potrebbe essere stato eseguito per molto tempo prima di scoprire una chiamata db ridondante che potrebbe essere molto costosa, e suggerisco di non dipendere completamente dall'impostazione dell'attributo MultipleActiveResultSets ma scoprire anche perché il codice ne ha bisogno dove ha fallito .


1

Molto probabilmente questo problema si verifica a causa della funzionalità di "caricamento lento" di Entity Framework. Di solito, a meno che non sia esplicitamente richiesto durante il recupero iniziale, tutti i dati uniti (tutto ciò che è archiviato in altre tabelle del database) vengono recuperati solo quando richiesto. In molti casi è una buona cosa, poiché impedisce di recuperare dati non necessari e quindi migliora le prestazioni delle query (senza join) e risparmia larghezza di banda.

Nella situazione descritta nella domanda, viene eseguito il recupero iniziale e durante la fase "select" vengono richiesti i dati di caricamento lazy mancanti, vengono eseguite ulteriori query e quindi EF si lamenta di "open DataReader".

La soluzione proposta nella risposta accettata consentirà l'esecuzione di queste query e l'intera richiesta avrà esito positivo.

Tuttavia, se esaminerai le richieste inviate al database, noterai più richieste - richiesta aggiuntiva per ogni dato mancante (caricato in modo pigro). Questo potrebbe essere un killer delle prestazioni.

Un approccio migliore consiste nel dire a EF di precaricare tutti i dati caricati lazy necessari durante la query iniziale. Questo può essere fatto usando l'istruzione "Include":

using System.Data.Entity;

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

In questo modo, verranno eseguiti tutti i join necessari e tutti i dati necessari verranno restituiti come un'unica query. Il problema descritto nella domanda sarà risolto.


Questa è una risposta valida, perché sono passato dall'utilizzo di Includi all'utilizzo di EntityEntry.Collection (). Load () e la mia soluzione è passata dal funzionamento al fallimento. Sfortunatamente, includere per un generico non è possibile "ThenInclude" un altro generico, quindi sto ancora cercando di far funzionare EntityEntry.Collection (). Load ().
AndrewBenjamin,

0

Sto usando il servizio web nel mio strumento, dove quei servizi recuperano la procedura memorizzata. mentre un numero maggiore di strumenti client recupera il servizio Web, questo problema si presenta. Ho risolto specificando l'attributo sincronizzato per quelle funzioni recupera la procedura memorizzata. ora funziona benissimo, l'errore non è mai apparso nel mio strumento.

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

Questo attributo consente di elaborare una richiesta alla volta. quindi questo risolve il problema.


0

Come nota a margine ... questo può accadere anche quando si verifica un problema con la mappatura dei dati (interna) da oggetti SQL.

Per esempio...

Ho creato un SQL Scalar Functionche ha restituito accidentalmente un VARCHAR... e poi ... l'ho usato per generare una colonna in un VIEW. La mappa VIEWera correttamente mappata nel DbContext... quindi Linq la stava chiamando bene. Tuttavia, l' Entity DateTime previsto ? e il VIEWritorno String .

Che ODDLY genera ...

"Esiste già un DataReader aperto associato a questo comando che deve essere chiuso per primo"

È stato difficile capire ... ma dopo aver corretto i parametri di ritorno ... tutto andava bene


0

Nel mio caso, ho dovuto impostare il MultipleActiveResultSetsto Truenella stringa di connessione.
Quindi è apparso un altro errore (quello reale) sul non essere in grado di eseguire 2 comandi (SQL) contemporaneamente nello stesso contesto di dati! (EF Core, prima il codice)
Quindi la soluzione per me era cercare qualsiasi altra esecuzione di comando asincrona e trasformarli in sincroni , dato che avevo un solo DbContext per entrambi i comandi.

Spero che ti aiuti

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.