Estou usando o Entity Framework e, ocasionalmente, recebo esse erro.
EntityCommandExecutionException
{"There is already an open DataReader associated with this Command which must be closed first."}
at System.Data.EntityClient.EntityCommandDefinition.ExecuteStoreCommands...
Mesmo que eu não esteja fazendo nenhum gerenciamento de conexão manual.
esse erro ocorre intermitentemente.
código que aciona o erro (abreviado para facilitar a leitura):
if (critera.FromDate > x) {
t= _tEntitites.T.Where(predicate).ToList();
}
else {
t= new List<T>(_tEntitites.TA.Where(historicPredicate).ToList());
}
usando Dispose pattern para abrir sempre uma nova conexão.
using (_tEntitites = new TEntities(GetEntityConnection())) {
if (critera.FromDate > x) {
t= _tEntitites.T.Where(predicate).ToList();
}
else {
t= new List<T>(_tEntitites.TA.Where(historicPredicate).ToList());
}
}
ainda problemático
por que a EF não reutilizaria uma conexão se ela já estiver aberta.
linq
entity-framework
sql-server-2008
Sonic Soul
fonte
fonte
predicate
ehistoricPredicate
variáveis. Eu descobri que se você passarFunc<T, bool>
paraWhere()
ele irá compilar e, às vezes, funcionar (porque faz o "onde" na memória). O que você deve fazer é passarExpression<Func<T, bool>>
paraWhere()
.Respostas:
Não se trata de fechar a conexão. A EF gerencia a conexão corretamente. Meu entendimento desse problema é que existem vários comandos de recuperação de dados executados em conexão única (ou comando único com várias seleções) enquanto o próximo DataReader é executado antes que o primeiro conclua a leitura. A única maneira de evitar a exceção é permitir vários DataReaders aninhados = ativar MultipleActiveResultSets. Outro cenário em que isso sempre acontece é quando você itera pelo resultado da consulta (IQueryable) e aciona o carregamento lento da entidade carregada na iteração.
fonte
Como alternativa ao uso do MARS (MultipleActiveResultSets), você pode escrever seu código para não abrir vários conjuntos de resultados.
O que você pode fazer é recuperar os dados na memória, para que você não abra o leitor. Isso geralmente é causado pela iteração em um conjunto de resultados ao tentar abrir outro conjunto de resultados.
Código de amostra:
Vamos dizer que você está fazendo uma pesquisa em seu banco de dados contendo estes:
Podemos fazer uma solução simples para isso adicionando .ToList () assim:
Isso força o framework de entidade a carregar a lista na memória; portanto, quando iteramos no loop foreach, ele não está mais usando o leitor de dados para abrir a lista, mas sim na memória.
Sei que isso pode não ser o desejado se você deseja carregar preguiçosamente algumas propriedades, por exemplo. Este é principalmente um exemplo que, com sorte, explica como / por que você pode ter esse problema, para que possa tomar decisões de acordo.
fonte
ToList
usando mil objetos, isso aumentará a memória em uma tonelada. Neste exemplo específico, seria melhor combinar a consulta interna com a primeira, para que apenas uma consulta seja gerada em vez de duas.Há outra maneira de superar esse problema. Se é uma maneira melhor, depende da sua situação.
O problema resulta do carregamento lento, portanto, uma maneira de evitá-lo é não ter um carregamento lento, através do uso de Incluir:
Se você usar os
Include
s apropriados , poderá evitar ativar o MARS. Mas se você perder um, receberá o erro, portanto, habilitar o MARS é provavelmente a maneira mais fácil de corrigi-lo.fonte
.Include
é uma solução muito melhor do que habilitar o MARS e muito mais fácil do que escrever seu próprio código de consulta SQL.Você recebe esse erro quando a coleção que você está tentando iterar é um tipo de carregamento lento (IQueriable).
Converter a coleção IQueriable em outra coleção enumerável resolverá esse problema. exemplo
Nota: .ToList () cria um novo conjunto sempre e pode causar problemas de desempenho se você estiver lidando com grandes dados.
fonte
SELECT COUNT(*) FROM Users
= 5Resolvi o problema facilmente (pragmático) adicionando a opção ao construtor. Assim, eu uso isso somente quando necessário.
fonte
Experimente na sua cadeia de conexão para definir
MultipleActiveResultSets=true
. Isso permite multitarefa no banco de dados.Isso funciona para mim ... se a sua conexão no app.config ou você a define programaticamente ... espero que seja útil
fonte
Originalmente, eu decidi usar um campo estático na minha classe API para fazer referência a uma instância do objeto MyDataContext (Onde MyDataContext é um objeto de Contexto EF5), mas foi isso que pareceu criar o problema. Adicionei código parecido com o seguinte a cada um dos meus métodos de API e isso corrigiu o problema.
Como outras pessoas declararam, os objetos EF Data Context NÃO são seguros para threads. Portanto, colocá-los no objeto estático acabará causando o erro "leitor de dados" nas condições corretas.
Minha suposição original era que a criação de apenas uma instância do objeto seria mais eficiente e proporcionaria um melhor gerenciamento de memória. Pelo que reuni pesquisando essa questão, esse não é o caso. De fato, parece ser mais eficiente tratar cada chamada de sua API como um evento isolado e seguro de thread. Garantir que todos os recursos sejam liberados corretamente, pois o objeto sai do escopo.
Isso faz sentido, especialmente se você levar sua API para a próxima progressão natural, que seria expor como API de WebService ou REST.
Divulgação
fonte
Percebi que esse erro ocorre quando envio um IQueriable para a exibição e o uso em um foreach duplo, onde o foreach interno também precisa usar a conexão. Exemplo simples (ViewBag.parents pode ser IQueriable ou DbSet):
A solução simples é usar
.ToList()
a coleção antes de usá-la. Observe também que o MARS não funciona com o MySQL.fonte
ToList()
primeira chamada para obter uma coleção do banco de dados. Então eu fiz umaforeach
lista e as chamadas subseqüentes funcionaram perfeitamente em vez de dar o erro.Eu descobri que tinha o mesmo erro e ocorreu quando eu estava usando um em
Func<TEntity, bool>
vez de umExpression<Func<TEntity, bool>>
para o seupredicate
.Depois que mudei tudo
Func's
paraExpression's
exceção deixou de ser lançada.Eu acredito que isso
EntityFramwork
faz algumas coisas inteligentes com asExpression's
quais simplesmente não se relacionaFunc's
fonte
(MyTParent model, Func<MyTChildren, bool> func)
para que meus ViewModels pudessem especificar uma determinadawhere
cláusula para o método Generic DataContext. Nada estava funcionando até eu fazer isso.2 soluções para mitigar esse problema:
.ToList()
após a consulta, para que você possa iterar através da abertura de um novo DataReader..Include
(/ entidades adicionais que você deseja carregar na consulta /) isso é chamado de carregamento rápido, o que permite (de fato) incluir objetos (entidades) associados durante a execução de uma consulta com o DataReader.fonte
Um bom meio termo entre ativar o MARS e recuperar todo o conjunto de resultados na memória é recuperar apenas IDs em uma consulta inicial e, em seguida, percorrer os IDs que materializam cada entidade à medida que você avança.
Por exemplo (usando as entidades de exemplo "Blog e postagens", como nesta resposta ):
Fazer isso significa que você apenas puxa alguns milhares de números inteiros para a memória, em oposição a milhares de gráficos de objetos inteiros, o que deve minimizar o uso da memória enquanto permite trabalhar item por item sem ativar o MARS.
Outro bom benefício disso, como visto na amostra, é que você pode salvar as alterações à medida que percorre cada item, em vez de ter que esperar até o final do loop (ou alguma outra solução alternativa), conforme seria necessário mesmo com MARS ativado (veja aqui e aqui ).
fonte
context.SaveChanges();
.. loop dentro :( Isso não é bom, deve ser fora do loop.No meu caso, descobri que faltavam instruções "aguardar" antes das chamadas myContext.SaveChangesAsync (). A adição de espera antes dessas chamadas assíncronas resolveu os problemas do leitor de dados para mim.
fonte
Se tentarmos agrupar parte de nossas condições em um método Func <> ou extensão, obteremos esse erro, suponha que tenhamos um código como este:
Isso lançará a exceção se tentarmos usá-lo em um Where (), o que devemos fazer é criar um Predicado como este:
Mais informações podem ser lidas em: http://www.albahari.com/nutshell/predicatebuilder.aspx
fonte
Esse problema pode ser resolvido simplesmente convertendo os dados em uma lista
fonte
Na minha situação, o problema ocorreu devido a um registro de injeção de dependência. Eu estava injetando um serviço de escopo por solicitação que estava usando um dbcontext em um serviço registrado singleton. Portanto, o dbcontext foi usado em várias solicitações e, portanto, o erro.
fonte
No meu caso, o problema não tinha nada a ver com a cadeia de conexão MARS, mas com a serialização json. Depois de atualizar meu projeto do NetCore2 para 3, recebi esse erro.
Mais informações podem ser encontradas aqui
fonte
Resolvi esse problema usando a seguinte seção de código antes da segunda consulta:
você pode alterar a hora de dormir em milissegundos
PD Útil ao usar threads
fonte