Por que é possível enumerar uma consulta dblinq depois de ligar para DisponE () no DataContext?
Pergunta
Atualizar - A resposta é aparentemente que o dblinq não implementa Dispose()
devidamente. D'HO!
O abaixo é todo tipo de enganador - Conclusão: o dblinq ainda não é (ainda) equivalente a Linqtosql, como presumi quando originalmente fiz essa pergunta. Use -o com cautela!
Estou usando o padrão de repositório com o dblinq. Meus objetos de repositório implementam IDisposable
, e as Dispose()
Método faz apenas coisa-calls Dispose()
no DataContext
. Sempre que uso um repositório, envolvi -o em um using
Bloco, como este:
public IEnumerable<Person> SelectPersons()
{
using (var repository = _repositorySource.GetPersonRepository())
{
return repository.GetAll(); // returns DataContext.Person as an IQueryable<Person>
}
}
Este método retorna um IEnumerable<Person>
, então, se meu entendimento estiver correto, nenhuma consulta do banco de dados realmente ocorre até Enumerable<Person>
é atravessado (por exemplo, convertendo -o em uma lista ou matriz ou usando -o em um foreach
loop), como neste exemplo:
var persons = gateway.SelectPersons();
// Dispose() is fired here
var personViewModels = (
from b in persons
select new PersonViewModel
{
Id = b.Id,
Name = b.Name,
Age = b.Age,
OrdersCount = b.Order.Count()
}).ToList(); // executes queries
Neste exemplo, Dispose()
é chamado imediatamente após a configuração persons
, que é um IEnumerable<Person>
, e essa é a única vez que é chamada.
Então, três perguntas:
- Como é que isso funciona? Como pode ser disposto
DataContext
ainda consulte o banco de dados para obter resultados após oDataContext
foi disposto? - O que
Dispose()
realmente faz? - Ouvi dizer que não é necessário (por exemplo, veja essa questão) para descartar um
DataContext
, mas minha impressão foi que não é uma má ideia. Existe algum motivo não Para descartar um dblinqDataContext
?
Solução
1 Como isso funciona? Como um datacontext disposto ainda pode consultar o banco de dados para obter resultados após a disposição do Datacontext?
Isto não trabalhar. Há algo que você não está nos mostrando. Acho que sua classe de repositório não descarta o DataContext
corretamente/na hora certa, ou que você está escrevendo perfurialamente ToList()
No final de cada consulta, que nega completamente a transformação da consulta e a execução diferida que você normalmente recebe.
Experimente o seguinte código em um aplicativo de teste, eu garantia você que vai jogar um ObjectDisposedException
:
// Bad code; do not use, will throw exception.
IEnumerable<Person> people;
using (var context = new TestDataContext())
{
people = context.Person;
}
foreach (Person p in people)
{
Console.WriteLine(p.ID);
}
Este é o caso reprodutível mais simples possível e sempre será lançado. Por outro lado, se você escrever people = context.Person.ToList()
Em vez disso, os resultados da consulta já foram enumerados lado de dentro a using
Bloco, o que aposto é o que está acontecendo no seu caso.
2 O que o dispete () realmente faz?
Entre outras coisas, ele define uma bandeira indicando que o DataContext
está disposto, que é verificado em todas as consultas subsequentes e causa o DataContext
para jogar um ObjectDisposedException
com a mensagem Object name: 'DataContext accessed after Dispose.'.
Também fecha a conexão, se o DataContext
Abriu e o deixou aberto.
3 Ouvi dizer que não é necessário (por exemplo, ver essa pergunta) descartar um datacontext, mas minha impressão foi que não é uma má idéia. Existe alguma razão para não descartar um Linqtosql Datacontext?
Isto é necessário para Dispose
a DataContext
, como é necessário Dispose
todos os outros IDisposable
. Você poderia potencialmente vazar conexões se não conseguir descartar o DataContext
. Você também pode vazar memória se alguma das entidades recuperadas do DataContext
são mantidos vivos, pois o contexto mantém um cache de identidade interno para o padrão de unidade de trabalho que implementa. Mas mesmo que nada disso fosse o caso, Não é sua preocupação o que Dispose
o método faz internamente. Suponha que faça algo importante.
IDisposable
é um contrato Isso diz: "A limpeza pode não ser automática; você precisa me descartar quando terminar". Você não tem garantias de se o objeto tem ou não seu próprio finalizador que se limpa depois de você se você esquecer Dispose
. As implementações estão sujeitas a alterações, e é por isso que não é uma boa idéia confiar no comportamento observado em oposição às especificações explícitas.
A pior coisa que pode acontecer se você descartar um IDisposable
com um vazio Dispose
O método é que você desperdiça alguns ciclos de CPU. A pior coisa que pode acontecer se você falhou para descartar um IDisposable
com um implementação não trivial é que você vaza recursos. A escolha aqui é óbvia; Se você vê um IDisposable
, não se esqueça de descartá -lo.
Outras dicas
"Pessoas" é uma coleção ienumerable, o DataContext (repositório) é necessário apenas para fazer a chamada .getNew.
As palavras -chave do/select/etc são açúcar sintático para métodos de extensão adicionados no espaço de nome System.linq. Esses métodos de extensão adicionam a funcionalidade IEnumerable que você está usando em sua consulta, não no DataContext. De fato, você pode fazer tudo isso sem usar o LINQ2SQL, criando programaticamente um iEnumerable para demonstrar.
Se você tentar fazer mais um repositório (Datacontext) mais chamadas usando esses objetos, é quando você receberá um erro.
A coleção IEnumerable conterá todos os registros do seu repositório, é por isso que você não precisa do DataContext para fazer a consulta.
Métodos de extensão: http://msdn.microsoft.com/en-us/library/bb383977.aspx
Métodos de extensão LINQ:http://msdn.microsoft.com/en-us/library/system.linq.enumerable_members.aspx
No fundo da API, você provavelmente verá um método usando uma API como esta:
http://msdn.microsoft.com/en-us/library/y6wy5a0f(v=vs.100).aspx
Quando o comando é executado, o objeto de conexão associado é fechado quando o objeto DataReader associado é fechado.