yield return statement dentro de um bloco using () {} Disposes antes de executar

Eu escrevi minha própria camada de dados personalizada para persistir em um arquivo específico e eu o abstracionei com um padrão DataContext personalizado.

Tudo isso é baseado no .NET 2.0 Framework (dadas restrições para o servidor de destino), portanto, mesmo que alguns deles pareçam com o LINQ para SQL, não é! Acabei de implementar um padrão de dados semelhante.

Veja o exemplo abaixo, por exemplo, de uma situação que ainda não posso explicar.

Para obter todas as instâncias de Animal – eu faço isso e funciona bem

public static IEnumerable GetAllAnimals() { AnimalDataContext dataContext = new AnimalDataContext(); return dataContext.GetAllAnimals(); } 

E a implementação do método GetAllAnimals () no AnimalDataContext () abaixo

 public IEnumerable GetAllAnimals() { foreach (var animalName in AnimalXmlReader.GetNames()) { yield return GetAnimal(animalName); } } 

O AnimalDataContext () implementa IDisposable porque eu tenho um XmlTextReader lá e quero ter certeza de que ele seja limpo rapidamente.

Agora, se eu embrulhar a primeira chamada dentro de uma declaração usando como assim

 public static IEnumerable GetAllAnimals() { using(AnimalDataContext dataContext = new AnimalDataContext()) { return dataContext.GetAllAnimals(); } } 

e coloque um ponto de quebra na primeira linha do método AnimalDataContext.GetAllAnimals () e outro ponto de quebra na primeira linha no método AnimalDataContext.Dispose (), e execute …

o método Dispose () é chamado PRIMEIRO, de forma que AnimalXmlReader.GetNames () forneça a exceção “referência de object não definida para instância de object” porque AnimalXmlReader foi definido como null no Dispose () ???

Alguma ideia? Eu tenho um palpite de que o seu relacionado ao retorno yield não pode ser chamado dentro de um bloco try-catch, que usando efetivamente representa, uma vez compilado …

Quando você chama GetAllAnimals , na verdade, ele não executa nenhum código até que você enumere o IEnumerable retornado em um loop foreach.

O dataContext está sendo descartado assim que o método wrapper retorna, antes de enumerar o IEnumerable.

A solução mais simples seria tornar o método wrapper um iterador também, assim:

 public static IEnumerable GetAllAnimals() { using (AnimalDataContext dataContext = new AnimalDataContext()) { foreach (var animalName in dataContext.GetAllAnimals()) { yield return GetAnimal(animalName); } } } 

Dessa forma, a instrução using será compilada no iterador externo e só será descartada quando o iterador externo for descartado.

Outra solução seria enumerar o IEnumerable no wrapper. A maneira mais simples de fazer isso seria retornar uma List , assim:

 public static IEnumerable GetAllAnimals() { using (AnimalDataContext dataContext = new AnimalDataContext()) { return new List(dataContext.GetAllAnimals()); } } 

Observe que isso perde o benefício da execução adiada, de modo que obterá todos os animais, mesmo que você não precise deles.

A razão para isso é que o método GetAllAnimals não retorna uma coleção de animais. Ele retorna um enumerador que é capaz de retornar um animal de cada vez.

Quando você retornar o resultado da chamada GetAllAnimals dentro do bloco using, basta retornar o enumerador. O bloco de utilização descarta o contexto de dados antes de o método sair e, nesse ponto, o enumerador ainda não leu nenhum animal. Quando você tenta usar o enumerador, ele não consegue obter nenhum animal do contexto de dados.

Uma solução alternativa é tornar o método GetAllAnimals também criar um enumerador. Dessa forma, o bloco de uso não será fechado até que você pare de usar esse enumerador:

 public static IEnumerable GetAllAnimals() { using(AnimalDataContext dataContext = new AnimalDataContext()) { foreach (Animal animal in dataContext.GetAllAnimals()) { yield return animal; } } }