2009-10-08 11 views
44

He escrito mi propia capa de datos a medida que persista a un archivo específico y he abstraído con una costumbre DataContext patrón.rendimiento sentencia return dentro de un bloque usando() {} Se deshace antes de ejecutar

esto se basa en el .NET Framework 2.0 (dadas las limitaciones para el servidor de destino), por lo que a pesar de que algunos de ellos podrían parecerse a LINQ a SQL, no es! Acabo de implementar un patrón de datos similar.

véase el ejemplo siguiente, por ejemplo de una situación que todavía no puedo explicar.

Para obtener todas las instancias de los animales - Lo hago y funciona método bien

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

Y la aplicación de las GetAllAnimals() en el AnimalDataContext() por debajo

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

El AnimalDataContext () implementa IDisposable porque tengo una XMLTextReader allí y quiero para asegurarse de que se limpie rápidamente.

Ahora bien, si envuelvo la primera llamada dentro de una instrucción using al igual que

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

y poner un punto de ruptura en la primera línea de los AnimalDataContext.GetAllAnimals() método y otro punto de ruptura en el primer línea en el método AnimalDataContext.Dispose(), y ejecutar ...

el método Dispose() se llama primero para que AnimalXmlReader.GetNames() da "referencia a objeto no establecida como instancia de objeto" porque tiene AnimalXmlReader se ha establecido en nulo en el Dispose() ???

¿Alguna idea? Tengo el presentimiento de que su regreso rendimiento relacionado con no que se les permita ser llamado dentro de un bloque try-catch, que usando representa efectivamente, una vez compilado ...

+0

Esto es básicamente uno de los problemas que he encontrado, así, ver a mi pregunta aquí: http://stackoverflow.com/questions/1524367 –

Respuesta

50

Cuando se llama a GetAllAnimals no lo hace realidad ejecutar ningún código hasta que enumera la IEnumerable devuelto en un bucle foreach.

El dataContext se elimina tan pronto como se devuelve el método de envoltura, antes de enumerar IEnumerable.

La solución más sencilla sería la de hacer que el método de envoltura en un iterador, así, como este:

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

De esta manera, se compilará la declaración utilizando en el iterador exterior, y que sólo se dispondrán cuando el iterador externo está dispuesto.

Otra solución sería enumerar el IEnumerable en el contenedor. La forma más sencilla de hacerlo sería para devolver un List<Animal>, así:

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

Tenga en cuenta que este pierde el beneficio de ejecución diferida, por lo que obtendrá todos los animales, incluso si usted no los necesita.

+1

Gracias SLaks - La segunda opción hizo el truco y es ordenada - ¡menos líneas de código, menos errores! Esa llamada entra en un "AnimalDataContextAdapter" que se encuentra en la capa de presentación de un proyecto de WebForms, específicamente para enumerar la colección, por lo que no hay pérdida real en la ejecución diferida. –

+0

Resumiría esto como: Nunca acepte parámetros IDisposable en métodos que usan 'yield return' u otros métodos de ejecución diferidos –

+0

El uso con' yield return' es seguro y llama a Dispose en su flujo [IFF] (https: // www. google.com/search?q=define%20iff) la colección se enumera por completo. http://blogs.msdn.com/b/dancre/archive/2008/03/14/yield-and-usings-your-dispose-may-not-be-called.aspx – yzorg

10

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 using, 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 consiste en hacer que el método GetAllAnimals también cree un enumerador. De esta forma el bloque usando no se cerrará hasta que deje de usar que empadronador:

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