statement - using namespace c#
declaraciĆ³n de retorno de rendimiento dentro de un bloque using(){} Se descarta antes de ejecutarse (2)
Cuando llama a GetAllAnimals
en realidad no ejecuta ningún código hasta que enumera el IEnumerable devuelto en un bucle foreach.
El dataContext se elimina tan pronto como regrese el método de envoltura, antes de enumerar el IEnumerable.
La solución más simple sería hacer que el método de contenedor también sea un iterador, como este:
public static IEnumerable<Animal> GetAllAnimals() {
using (AnimalDataContext dataContext = new AnimalDataContext()) {
foreach (var animalName in dataContext.GetAllAnimals()) {
yield return GetAnimal(animalName);
}
}
}
De esta forma, la instrucción using se compilará en el iterador externo, y solo se eliminará cuando se elimine el iterador externo.
Otra solución sería enumerar el IEnumerable en el contenedor. La forma más sencilla de hacer eso sería devolver una List<Animal>
, como esta:
public static IEnumerable<Animal> GetAllAnimals() {
using (AnimalDataContext dataContext = new AnimalDataContext()) {
return new List<Animal>(dataContext.GetAllAnimals());
}
}
Tenga en cuenta que esto pierde el beneficio de la ejecución diferida, por lo que obtendrá todos los animales, incluso si no los necesita.
He escrito mi propia capa de datos personalizada para que persista en un archivo específico y la he abstraído con un patrón de DataContext personalizado.
Todo esto se basa en .NET 2.0 Framework (restricciones dadas para el servidor de destino), por lo que aunque algunas de ellas parezcan LINQ-to-SQL, ¡no es así! Acabo de implementar un patrón de datos similar.
Vea el ejemplo a continuación, por ejemplo, de una situación que aún no puedo explicar.
Para obtener todas las instancias de Animal, hago esto y funciona bien
public static IEnumerable<Animal> GetAllAnimals() {
AnimalDataContext dataContext = new AnimalDataContext();
return dataContext.GetAllAnimals();
}
Y la implementación del método GetAllAnimals () en AnimalDataContext () a continuación
public IEnumerable<Animal> GetAllAnimals() {
foreach (var animalName in AnimalXmlReader.GetNames())
{
yield return GetAnimal(animalName);
}
}
El AnimalDataContext () implementa IDisposable porque tengo un XmlTextReader y quiero asegurarme de que se limpia rápidamente.
Ahora si envuelvo la primera llamada dentro de una declaración de uso como tal
public static IEnumerable<Animal> GetAllAnimals() {
using(AnimalDataContext dataContext = new AnimalDataContext()) {
return dataContext.GetAllAnimals();
}
}
y poner un punto de quiebre en la primera línea del método AnimalDataContext.GetAllAnimals () y otro punto de quiebre en la primera línea en el método AnimalDataContext.Dispose (), y ejecutar ...
el método Dispose () se llama FIRST para que AnimalXmlReader.GetNames () proporcione la excepción "object reference not set to event of object" porque AnimalXmlReader se ha establecido como nulo en el Dispose () ???
¿Algunas ideas? Tengo la corazonada de que no está permitido que se invoque el retorno de rendimiento dentro de un bloque try-catch, que el uso representa efectivamente, una vez compilado ...
La razón de esto es que el método GetAllAnimals no devuelve una recopilación de animales. Devuelve un enumerador que es capaz de devolver un animal a la vez.
Cuando devuelve el resultado de la llamada GetAllAnimals dentro del bloque que usa, simplemente devuelve el enumerador. El bloque de uso elimina el contexto de datos antes de que el método salga, y en ese momento el enumerador aún no ha leído ningún animal. Cuando intenta utilizar el enumerador, no puede obtener ningún animal del contexto de datos.
Una solución alternativa es hacer que el método GetAllAnimals también cree un enumerador. De esa forma, el bloque de uso no se cerrará hasta que deje de usar ese enumerador:
public static IEnumerable<Animal> GetAllAnimals() {
using(AnimalDataContext dataContext = new AnimalDataContext()) {
foreach (Animal animal in dataContext.GetAllAnimals()) {
yield return animal;
}
}
}