Já existe um DataReader aberto associado a este comando que deve ser fechado primeiro

641

Eu tenho essa consulta e recebo o erro nesta função:

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

O erro é:

Já existe um DataReader aberto associado a este comando que deve ser fechado primeiro.

Atualizar:

rastreamento de pilha adicionado:

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
fonte

Respostas:

1288

Isso pode acontecer se você executar uma consulta enquanto itera sobre os resultados de outra consulta. Não está claro no seu exemplo onde isso acontece porque o exemplo não está completo.

Uma coisa que pode causar isso é o carregamento lento acionado ao iterar sobre os resultados de alguma consulta.

Isso pode ser facilmente resolvido, permitindo MARS na sua cadeia de conexão. Adicione MultipleActiveResultSets=trueao fornecedor parte da sua cadeia de conexão (onde Fonte de dados, Catálogo inicial, etc. são especificados).

Ladislav Mrnka
fonte
34
Isso funcionou para mim. Se você quiser ler mais sobre a habilitação de vários conjuntos de resultados ativos (MARS), consulte msdn.microsoft.com/en-us/library/h32h3abf(v=vs.100).aspx . Considere ler também as Desvantagens do MARS stackoverflow.com/questions/374444/…
Diganta Kumar 12/12/12 /
3
Levando em consideração o desempenho, você também pode resolver isso incluindo System.Data.Entity e, em seguida, usando as instruções Include para garantir que esses dados secundários sejam carregados na consulta original. Se você ativar o MARS, desativá-lo para verificar essas cargas repetidas de dados pode ajudar a acelerar suas chamadas de processamento de dados, reduzindo as viagens de ida e volta.
Chris Moschini
70
A ativação do MARS deve ser feita apenas para um subconjunto muito pequeno de problemas / casos de uso. Na maioria dos casos, o erro em questão é causado pelo BAD CODE no aplicativo de chamada. Mais detalhes aqui: devproconnections.com/development/…
Michael K. Campbell
132
Adicionar .ToList () após o seu.Include (). Where () provavelmente resolverá o problema.
Serj Sagan 31/10
2
Fazer uma conexão SQL global com grandes alterações em uma consulta é ridículo. A resposta correta deve ser a ToList abaixo. Uma correção local (ou seja, basta alterar a consulta) para um problema localizado!
Bydevev
218

Você pode usar o ToList()método antes da returndeclaração.

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
fonte
9
Eu já tive esse erro tantas vezes agora ... e sempre que esqueço! A resposta para a pergunta é sempre usar ToList ().
Cheesus Toast
1
Há alguma desvantagem nisso? Se você tiver 100 mil linhas, duvido que isso possa ser bom.
Martin Dawson
2
@MartinMazzaDawson, você realmente precisa de 100K registros ao mesmo tempo para execução de consultas ?? Eu acho que, usando a paginação é uma boa idéia para esta situação
kazem
desculpe-me por levantar um tópico antigo, mas encontrei o mesmo erro ao desenvolver um RepositoryPattern e o resolvi adicionando ".ToList () ou Single () ou Count ()" a todos os métodos do Repositório. Enquanto no começo eu estava retornando ".AsEnumerable ()". Agora a minha pergunta é: se o repositório de devolver o "ToList ()", ou isso é algo que shold ser exigido ao consumidor final (ou seja: a lógica de serviço / negócio)
alessalessio
Funciona para mim. Adicionar .ToList resolve o problema do problema de suporte decimal no JetEntityFrameworkProvider. Total = storeDb.OF_Carts.Where(x => x.CartId == ShoppingCartId).ToList().Sum(t => t.Quantity * t.Item.UnitPrice);
hubert17
39

use a sintaxe .ToList()para converter a leitura de objeto do db na lista, para evitar ser relida novamente. Espero que isso funcione para isso. Obrigado.

Icemark Muturi
fonte
22

Aqui está uma sequência de conexões de trabalho para alguém que precisa de referência.

  <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
fonte
15
A ativação do MARS é uma solução alternativa, NÃO uma solução para o problema.
SandRock 26/11
5
Na página Documentação do MARS: "As operações do MARS não são seguras para threads". Isso significa que, se o problema surgir de vários threads acessando o Contexto, o MARS (provavelmente) não será a solução.
Marsop
20

No meu caso, usar Include()esse erro foi resolvido e, dependendo da situação, pode ser muito mais eficiente do que emitir várias consultas quando todas elas podem ser consultadas ao mesmo tempo com uma junção.

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
fonte
Esta é a melhor solução se seu aplicativo não exigir o MARS.
Fred Wilson
7

Não sei se é uma resposta duplicada ou não. Se for, me desculpe. Eu só quero que os necessitados saibam como resolvi meu problema usando o ToList ().

No meu caso, eu tenho a mesma exceção para a consulta abaixo.

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

Eu resolvi como abaixo

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

Parece que você está chamando DateLastUpdated de uma consulta ativa usando o mesmo contexto EF e DateLastUpdate emite um comando para o próprio armazenamento de dados. O Entity Framework suporta apenas um comando ativo por contexto por vez.

Você pode refatorar suas duas consultas acima em uma 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);

Também notei que você está chamando funções como FormattedAccountNumber e FormattedRecordNumber nas consultas. A menos que sejam procs ou funções armazenadas importadas do banco de dados para o modelo de dados da entidade e mapeadas corretamente, elas também geram excertos, pois a EF não sabe como traduzir essas funções em instruções que podem ser enviadas para o armazenamento de dados.

Observe também que chamar AsEnumerable não força a execução da consulta. Até que a execução da consulta seja adiada até enumerada. Você pode forçar a enumeração com ToList ou ToArray, se desejar.

James Alexander
fonte
Se desejar, você pode refatorar a consulta que está realizando para obter o DateLastUpdated diretamente em sua projeção Selecionar para a consulta Relatório de contas e obter o efeito desejado sem o erro.
James Alexander
Estou recebendo o mesmo erro depois de colocar o código da função na consulta principal
DotnetSparrow
2

Além da resposta de Ladislav Mrnka :

Se você estiver publicando e substituindo o contêiner na guia Configurações , poderá definir MultipleActiveResultSet como True. Você pode encontrar essa opção clicando em Avançado ... e ele estará no grupo Avançado .

Alexander Troshchenko
fonte
2

Para quem encontra isso via Google;
Eu estava recebendo esse erro porque, conforme sugerido pelo erro, não consegui fechar um SqlDataReader antes de criar outro no mesmo SqlCommand, assumindo por engano que seria coletado lixo ao sair do método em que ele foi criado.

Resolvi o problema ligando sqlDataReader.Close();antes de criar o segundo leitor.

timelmer
fonte
2

No meu caso, eu abri uma consulta no contexto de dados, como

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

... e, posteriormente, consultou o mesmo ...

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

Adicionar o .ToListprimeiro resolveu o meu problema. Eu acho que faz sentido envolver isso em uma propriedade 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

Onde _stores é uma variável privada e Filters também é uma propriedade somente leitura que lê do AppSettings.

Adam Cox
fonte
1

Eu tive o mesmo erro, quando tentei atualizar alguns registros no loop de leitura. Tentei a resposta mais votada MultipleActiveResultSets=truee descobri que é apenas uma solução alternativa para obter o próximo erro 

Nova transação não é permitida porque há outros threads em execução na sessão

A melhor abordagem que funcionará para enormes ResultSets é usar blocos e abrir um contexto separado para cada bloco, conforme descrito em  SqlException do Entity Framework - Nova transação não é permitida porque existem outros threads em execução na sessão

Michael Freidgeim
fonte
1

Resolvi esse problema alterando Aguardar _accountSessionDataModel.SaveChangesAsync (); para _accountSessionDataModel.SaveChanges (); na minha aula de repositório.

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

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

Alterou para:

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

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

O problema foi que atualizei as sessões no front-end após criar uma sessão (no código), mas como SaveChangesAsync ocorre de forma assíncrona, a busca das sessões causou esse erro porque, aparentemente, a operação SaveChangesAsync ainda não estava pronta.

woutercx
fonte
1

Bem, para mim, foi meu próprio bug. Eu estava tentando executar um INSERTuso SqlCommand.executeReader()quando deveria estar usando SqlCommand.ExecuteNonQuery(). Foi aberto e nunca fechado, causando o erro. Cuidado com essa supervisão.

Andrew Taylor
fonte
Foi o mesmo problema do meu lado. Eu precisava de SqlCommand.executeReader () porque estou recebendo o ID de linhas inseridas. Então: eu usei SqlDataReader.Close (); Sql Command.Dispose (); Obrigado @Andrew Taylor
Fuat
1

Isso é extraído de um cenário do mundo real:

  • O código funciona bem em um ambiente Stage com MultipleActiveResultSets definido na cadeia de conexão
  • Código publicado no ambiente de produção sem MultipleActiveResultSets = true
  • Tantas páginas / chamadas funcionam enquanto uma única falha
  • Olhando mais de perto a chamada, há uma chamada desnecessária feita no banco de dados e precisa ser removida
  • Defina MultipleActiveResultSets = true em Production e publique código limpo, tudo funciona bem e, com eficiência

Em conclusão, sem esquecer o MultipleActiveResultSets, o código pode ter sido executado por um longo tempo antes de descobrir uma chamada db redundante que pode ser muito cara, e sugiro não depender totalmente da configuração do atributo MultipleActiveResultSets, mas também descobrir por que o código precisa dele onde falhou .

usefulBee
fonte
1

Provavelmente, esse problema ocorre devido ao recurso "carregamento lento" do Entity Framework. Normalmente, a menos que seja explicitamente necessário durante a busca inicial, todos os dados associados (qualquer coisa armazenada em outras tabelas do banco de dados) são buscados apenas quando necessário. Em muitos casos, isso é bom, pois impede a obtenção de dados desnecessários e, assim, melhora o desempenho da consulta (sem junções) e economiza largura de banda.

Na situação descrita na pergunta, a busca inicial é executada e, durante a fase "select", são solicitados dados de carregamento lento ausentes, consultas adicionais são emitidas e a EF está reclamando do "open DataReader".

A solução alternativa proposta na resposta aceita permitirá a execução dessas consultas e, de fato, toda a solicitação será bem-sucedida.

No entanto, se você examinar as solicitações enviadas ao banco de dados, notará várias solicitações - solicitação adicional para cada dado ausente (carregado com atraso). Isso pode ser um fator que mata o desempenho.

Uma abordagem melhor é informar ao EF para pré-carregar todos os dados carregados preguiçosos necessários durante a consulta inicial. Isso pode ser feito usando a instrução "Incluir":

using System.Data.Entity;

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

Dessa forma, todas as junções necessárias serão executadas e todos os dados necessários serão retornados como uma única consulta. O problema descrito na pergunta será resolvido.

Illidan
fonte
Esta é uma resposta válida, porque passei de Incluir para EntityEntry.Collection (). Load () e minha solução passou de trabalho para quebrada. Infelizmente, incluir para um genérico não pode "ThenInclude" outro genérico, então ainda estou tentando fazer EntityEntry.Collection (). Load () funcionar.
AndrewBenjamin
0

Estou usando o serviço web na minha ferramenta, onde esses serviços buscam o procedimento armazenado. enquanto um número maior de ferramentas cliente busca o serviço da web, esse problema surge. Eu corrigi especificando o atributo Synchronized para essas funções busca o procedimento armazenado. agora está funcionando bem, o erro nunca apareceu na minha ferramenta.

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

Este atributo permite processar uma solicitação por vez. então isso resolve o problema.

Pranesh Janarthanan
fonte
0

Como uma observação lateral ... isso também pode acontecer quando há um problema com o mapeamento de dados (interno) do SQL Objects.

Por exemplo...

Eu criei um SQL Scalar Functionque acidentalmente retornou um VARCHAR... e então ... usei para gerar uma coluna em um VIEW. O VIEWfoi corretamente mapeado no DbContext... então o Linq estava chamando de muito bom. No entanto, a entidade esperava DateTime? e o VIEWestava retornando String .

Que estranhamente joga ...

"Já existe um DataReader aberto associado a este comando que deve ser fechado primeiro"

Foi difícil descobrir ... mas depois que eu corrigi os parâmetros de retorno ... tudo estava bem

Prisioneiro ZERO
fonte
0

No meu caso, eu tinha que definir o MultipleActiveResultSetsque Truena seqüência de conexão.
Então apareceu outro erro (o real) sobre não poder executar 2 comandos (SQL) ao mesmo tempo no mesmo contexto de dados! (EF Core, código primeiro)
Portanto, a solução para mim foi procurar qualquer outra execução de comando assíncrono e transformá-las em síncronas , pois eu tinha apenas um DbContext para os dois comandos.

Espero que ajude você

Dr TJ
fonte