Ya hay un DataReader abierto asociado con este comando que debe cerrarse primero

641

Tengo esta consulta y recibo el error en esta función:

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

El error es:

Ya hay un DataReader abierto asociado con este comando que debe cerrarse primero.

Actualizar:

seguimiento de pila agregado:

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
fuente

Respuestas:

1288

Esto puede suceder si ejecuta una consulta mientras itera sobre los resultados de otra consulta. No está claro en su ejemplo dónde sucede esto porque el ejemplo no está completo.

Una cosa que puede causar esto es la carga diferida que se activa al iterar sobre los resultados de alguna consulta.

Esto se puede resolver fácilmente permitiendo MARS en su cadena de conexión. Agregue MultipleActiveResultSets=truea la parte del proveedor de su cadena de conexión (donde se especifican Fuente de datos, Catálogo inicial, etc.).

Ladislav Mrnka
fuente
34
Esto funcionó para mí. Si desea obtener más información sobre cómo habilitar conjuntos de resultados activos múltiples (MARS), consulte msdn.microsoft.com/en-us/library/h32h3abf(v=vs.100).aspx . Considere leer sobre las desventajas de MARS también stackoverflow.com/questions/374444/…
Diganta Kumar
3
Teniendo en cuenta el rendimiento, también puede resolver esto si incluye System.Data.Entity y luego usa las declaraciones de inclusión para garantizar que estos datos secundarios se carguen en la consulta original. Si habilita MARS, desactivarlo para verificar estas cargas repetidas de datos puede ayudar a acelerar sus llamadas de procesamiento de datos al reducir los viajes de ida y vuelta.
Chris Moschini
70
La habilitación de MARS solo debe hacerse para un subconjunto muy pequeño de problemas / casos de uso. En la mayoría de los casos, el error en cuestión es causado por un CÓDIGO MALO dentro de la aplicación que realiza la llamada. Más detalles aquí: devproconnections.com/development/…
Michael K. Campbell
132
Agregar .ToList () después de your.Include (). Where () probablemente resolverá el problema.
Serj Sagan el
2
Para hacer una conexión SQL global, un cambio amplio para una consulta es ridículo. La respuesta correcta debe ser la lista de abajo. ¡Una solución local (es decir, simplemente cambie la consulta) para un problema localizado!
bytedev
218

Puede usar el ToList()método antes de la returndeclaración.

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
fuente
99
He tenido este error tantas veces ... ¡y cada vez que lo olvido! La respuesta a la pregunta siempre es usar ToList ().
Cheesus Toast
1
¿Hay alguna desventaja en esto? Si tiene 100k filas, dudo que esto pueda ser bueno.
Martin Dawson
2
@MartinMazzaDawson, ¿Realmente necesitas 100K registros a la vez? Creo que usar la paginación es una buena idea para esta situación
kazem
lamento plantear un tema antiguo, pero me encontré con el mismo error al desarrollar un RepositoryPattern y lo resolví agregando ".ToList () o Single () o Count ()" a cada método del Repository. Mientras que al principio estaba regresando ".AsEnumerable ()". Ahora mi pregunta es: si el repositorio devuelve "ToList ()", o es algo que debe exigirse al consumidor final (es decir: la lógica de servicio / negocio)
alessalessio
Funciona para mi. Agregar .ToList resuelve el problema del soporte de soporte decimal en JetEntityFrameworkProvider. Total = storeDb.OF_Carts.Where(x => x.CartId == ShoppingCartId).ToList().Sum(t => t.Quantity * t.Item.UnitPrice);
hubert17
39

use la sintaxis .ToList()para convertir el objeto leído de db a la lista para evitar que se vuelva a leer de nuevo. Espero que esto funcione. Gracias.

Icemark Muturi
fuente
22

Aquí hay una cadena de conexión que funciona para alguien que necesita referencia.

  <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
fuente
15
Habilitar MARS es una solución alternativa, NO una solución al problema.
SandRock
55
Desde la página de documentación de MARS: "Las operaciones de MARS no son seguras para subprocesos". Eso significa que, si el problema surge de múltiples hilos que acceden al Contexto, MARS (probablemente) no es la solución.
Marsop
20

En mi caso, usar Include()este error resuelto y, dependiendo de la situación, puede ser mucho más eficiente que emitir múltiples consultas cuando todo se puede consultar a la vez con una unión.

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
fuente
Esta es la mejor solución si su aplicación no requiere MARS.
Fred Wilson
7

No sé si esta es una respuesta duplicada o no. Si es así lo siento. Solo quiero que los necesitados sepan cómo resolví mi problema usando ToList ().

En mi caso, obtuve la misma excepción para la consulta a continuación.

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

Resolví como abajo

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

Parece que está llamando a DateLastUpdated desde una consulta activa usando el mismo contexto EF y DateLastUpdate emite un comando para el almacén de datos. Entity Framework solo admite un comando activo por contexto a la vez.

Puede refactorizar sus dos consultas anteriores en una como esta:

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

También noté que estás llamando a funciones como FormattedAccountNumber y FormattedRecordNumber en las consultas. A menos que estos sean procesos almacenados o funciones que haya importado de su base de datos en el modelo de datos de la entidad y correlacionado correctamente, también arrojarán excepciones ya que EF no sabrá cómo traducir esas funciones en declaraciones que puede enviar al almacén de datos.

También tenga en cuenta que llamar a AsEnumerable no obliga a ejecutar la consulta. Hasta que la ejecución de la consulta se difiera hasta que se enumere. Puede forzar la enumeración con ToList o ToArray si así lo desea.

James Alexander
fuente
Si lo desea, puede refactorizar la consulta que está realizando para obtener la dirección DateLastUpdated en su proyección Seleccionar para la consulta Informe de cuentas y obtener el efecto deseado sin el error.
James Alexander
Estoy consiguiendo el mismo error después de poner el código de función dentro consulta principal
DotnetSparrow
2

Además de la respuesta de Ladislav Mrnka :

Si publica y anula el contenedor en la pestaña Configuración , puede establecer MultipleActiveResultSet en True. Puede encontrar esta opción haciendo clic en Avanzado ... y estará en el grupo Avanzado .

Alexander Troshchenko
fuente
2

Para aquellos que encuentren esto a través de Google;
Estaba recibiendo este error porque, como lo sugirió el error, no pude cerrar un SqlDataReader antes de crear otro en el mismo SqlCommand, suponiendo erróneamente que sería basura recolectada al abandonar el método en el que se creó.

Resolví el problema llamando sqlDataReader.Close();antes de crear el segundo lector.

timelmer
fuente
2

En mi caso, había abierto una consulta desde el contexto de datos, como

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

... y luego consultó lo mismo ...

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

Agregar el .ToListal primero resolvió mi problema. Creo que tiene sentido envolver esto en una propiedad como:

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

Donde _stores es una variable privada y Filters también es una propiedad de solo lectura que se lee desde AppSettings.

Adam Cox
fuente
1

Tuve el mismo error cuando intenté actualizar algunos registros dentro del ciclo de lectura. Intenté la respuesta más votada MultipleActiveResultSets=truey descubrí que es solo una solución para obtener el siguiente error 

No se permite una nueva transacción porque hay otros subprocesos ejecutándose en la sesión

El mejor enfoque, que funcionará para los ResultSets enormes es usar fragmentos y abrir un contexto separado para cada fragmento como se describe en  SqlException de Entity Framework: no se permite una nueva transacción porque hay otros hilos ejecutándose en la sesión

Michael Freidgeim
fuente
1

Resolví este problema cambiando aguardar _accountSessionDataModel.SaveChangesAsync (); a _accountSessionDataModel.SaveChanges (); en mi clase de repositorio.

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

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

Lo cambió a:

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

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

El problema fue que actualicé las Sesiones en el frontend después de crear una sesión (en código), pero debido a que SaveChangesAsync ocurre de forma asincrónica, la recuperación de las sesiones causó este error porque aparentemente la operación SaveChangesAsync aún no estaba lista.

woutercx
fuente
1

Bueno para mí fue mi propio error. Estaba tratando de ejecutar un INSERTuso SqlCommand.executeReader()cuando debería haberlo estado usando SqlCommand.ExecuteNonQuery(). Fue abierto y nunca cerrado, causando el error. Cuidado con este descuido.

Andrew Taylor
fuente
Fue el mismo problema de mi parte. Necesitaba SqlCommand.executeReader () porque recibo ID de filas insertadas. Entonces: utilicé SqlDataReader.Close (); Sql Command.Dispose (); Gracias @ Andrew Taylor
Fuat
1

Esto se extrae de un escenario del mundo real:

  • El código funciona bien en un entorno Stage con MultipleActiveResultSets establecido en la cadena de conexión
  • Código publicado en el entorno de producción sin MultipleActiveResultSets = true
  • Tantas páginas / llamadas funcionan mientras falla una sola
  • Mirando más de cerca la llamada, hay una llamada innecesaria hecha a la base de datos y necesita ser eliminada
  • Establezca MultipleActiveResultSets = true en Producción y publique el código limpio, todo funciona bien y de manera eficiente

En conclusión, sin olvidar los MultipleActiveResultSets, el código podría haberse ejecutado durante mucho tiempo antes de descubrir una llamada db redundante que podría ser muy costosa, y sugiero no depender por completo de la configuración del atributo MultipleActiveResultSets pero también descubrir por qué el código lo necesita donde falló .

útilBee
fuente
1

Lo más probable es que este problema ocurra debido a la función de "carga diferida" de Entity Framework. Por lo general, a menos que se requiera explícitamente durante la recuperación inicial, todos los datos unidos (cualquier cosa que se haya almacenado en otras tablas de la base de datos) se recuperan solo cuando es necesario. En muchos casos, eso es bueno, ya que evita obtener datos innecesarios y, por lo tanto, mejora el rendimiento de la consulta (sin uniones) y ahorra ancho de banda.

En la situación descrita en la pregunta, se realiza la búsqueda inicial, y durante la fase "selectiva" se solicitan datos de carga diferida que faltan, se emiten consultas adicionales y luego EF se queja de "DataReader abierto".

La solución propuesta en la respuesta aceptada permitirá la ejecución de estas consultas y, de hecho, toda la solicitud tendrá éxito.

Sin embargo, si examina las solicitudes enviadas a la base de datos, notará múltiples solicitudes: solicitud adicional para cada dato faltante (carga lenta). Esto podría ser un asesino de rendimiento.

Un mejor enfoque es decirle a EF que precargue todos los datos de carga lenta necesarios durante la consulta inicial. Esto se puede hacer usando la declaración "Incluir":

using System.Data.Entity;

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

De esta forma, se realizarán todas las uniones necesarias y todos los datos necesarios se devolverán como una sola consulta. El problema descrito en la pregunta será resuelto.

Illidan
fuente
Esta es una respuesta válida, porque pasé de usar Incluir a usar EntityEntry.Collection (). Load (), y mi solución pasó de funcionar a rota. Desafortunadamente, incluir para un genérico no puede "ThenInclude" otro genérico, por lo que todavía estoy tratando de hacer que EntityEntry.Collection (). Load () funcione.
AndrewBenjamin
0

Estoy usando el servicio web en mi herramienta, donde esos servicios obtienen el procedimiento almacenado. Si bien una mayor cantidad de herramientas de cliente obtiene el servicio web, surge este problema. Lo he solucionado especificando el atributo Sincronizado para esas funciones recupera el procedimiento almacenado. ahora funciona bien, el error nunca apareció en mi herramienta.

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

Este atributo permite procesar una solicitud a la vez. entonces esto resuelve el problema.

Pranesh Janarthanan
fuente
0

Como nota al margen ... esto también puede suceder cuando hay un problema con el mapeo de datos (interno) de los objetos SQL.

Por ejemplo...

Creé un SQL Scalar Functionque accidentalmente devolvió un VARCHAR... y luego ... lo usé para generar una columna en a VIEW. El VIEWestaba correctamente mapeado en DbContext... así que Linq lo estaba llamando bien. Sin embargo, ¿la entidad esperaba DateTime? y VIEWestaba regresando String .

Que ODDLY arroja ...

"Ya hay un DataReader abierto asociado con este comando que debe cerrarse primero"

Fue difícil de descifrar ... pero después de corregir los parámetros de retorno ... todo estuvo bien

Prisionero CERO
fuente
0

En mi caso, tuve que configurar el MultipleActiveResultSets a Trueen la cadena de conexión.
¡Entonces apareció otro error (el real) sobre no poder ejecutar 2 comandos (SQL) al mismo tiempo sobre el mismo contexto de datos! (EF Core, Code first)
Así que la solución para mí fue buscar cualquier otra ejecución de comando asincrónica y convertirla en síncrona , ya que solo tenía un DbContext para ambos comandos.

Espero que te ayude

Dr. TJ
fuente