2010-05-17 7 views
7

Actualización - La respuesta es aparentemente que DbLinq no implementa Dispose() correctamente. D'oh!¿Por qué es posible enumerar una consulta DbLinq después de llamar a Dispose() en el DataContext?


El abajo es todo tipo de engañosa - En pocas palabras: DbLinq (todavía) no es equivalente a LinqToSql, ya que asumí cuando originalmente hice esta pregunta. Úselo con precaución!

Estoy usando el patrón de repositorio con DbLinq. Mis objetos de repositorio implementan IDisposable, y el método Dispose() solo hace una cosa: llama al Dispose() en el DataContext. Cada vez que utilice un repositorio, que lo envuelve en un bloque using, así:

public IEnumerable<Person> SelectPersons() 
{ 
    using (var repository = _repositorySource.GetPersonRepository()) 
    { 
     return repository.GetAll(); // returns DataContext.Person as an IQueryable<Person> 
    } 
} 

Este método devuelve un IEnumerable<Person>, por lo que si mi interpretación es correcta, sin consulta de la base de datos tiene lugar realmente hasta Enumerable<Person> es atravesado (por ejemplo, mediante la conversión a una lista o matriz o mediante su uso en un bucle foreach), como en este ejemplo:

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 

en este ejemplo, Dispose() es llamado inmediatamente después de establecer persons, que es un IEnumerable<Person>, y eso es el único momento en que se llama.

Así, tres preguntas:

  1. ¿Cómo funciona esto? ¿Cómo se puede eliminar un DataContext aún consultar la base de datos para obtener resultados después de que se haya eliminado el DataContext?
  2. ¿Qué hace realmente Dispose()?
  3. He oído que no es necesario (por ejemplo, consulte this question) deshacerse de un DataContext, pero mi impresión es que no es una mala idea. ¿Hay alguna razón no para desechar un DbLinq DataContext?
+0

¿Qué hace el método 'repository.GetAll()'? ¿Qué devuelve? –

+0

@ Eclipsed4utoo, buena pregunta. Comenté el código. – devuxer

Respuesta

3

1 ¿Cómo funciona? ¿Cómo puede un DataContext eliminado consultar en la base de datos los resultados una vez que se ha eliminado el DataContext?

Es no trabajo. Hay algo que no nos estás mostrando. Supongo que su clase de repositorio no dispone DataContext correctamente/en el momento correcto o que está escribiendo ToList() al final de cada consulta, lo que niega completamente la transformación de la consulta y la ejecución diferida que normalmente obtiene.

Prueba el código siguiente en una aplicación de prueba, yo garantizo usted que va a lanzar una 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 es el caso reproducible más simple posible, y siempre a tirar. Por otro lado, si escribe people = context.Person.ToList() en su lugar, los resultados de la consulta ya se han enumerado dentro del bloqueusing, que apostaría a lo que está sucediendo en su caso.

2 ¿Qué hace Dispose() en realidad?

Entre otras cosas, se establece un indicador que indica que la DataContext está dispuesto, que se comprueba en cada consulta posterior y hace que el DataContext a lanzar una ObjectDisposedException con el mensaje Object name: 'DataContext accessed after Dispose.'.

También cierra la conexión, si el DataContext lo abrió y lo dejó abierto.

3 He oído que no es necesario (por ejemplo, ver esta pregunta) deshacerse de un DataContext, pero mi impresión es que no es una mala idea. ¿Hay alguna razón para no disponer de un LinqToSql DataContext?

Se es necesarioDispose la DataContext, ya que es necesario DisposeIDisposable todos los demás. Podría perder conexiones si no puede eliminar el DataContext. También podría perder memoria si alguna de las entidades recuperadas del DataContext se mantienen activas, ya que el contexto mantiene un caché de identidad interno para el patrón de unidad de trabajo que implementa. Pero incluso si nada de esto fuera el caso, no le concierne lo que el método Dispose hace internamente. Supongamos que hace algo importante.

IDisposable es un contrato que dice: "la limpieza puede no ser automática; debe desecharme cuando haya terminado". No tiene garantías de si el objeto tiene o no su propio finalizador que se limpia después de usted si se olvida de Dispose. Las implementaciones están sujetas a cambios, por lo que no es una buena idea confiar en el comportamiento observado en comparación con las especificaciones explícitas.

Lo peor que puede pasar si dispone de un IDisposable con un método Dispose vacío es que desperdicia algunos ciclos de CPU. Lo peor que puede pasar si falla para eliminar un IDisposable con una implementación no trivial es que pierde recursos. La elección aquí es obvia; si ve un IDisposable, no olvide desecharlo.

+1

Bueno, estoy muy contento de haber hecho esta pregunta. Y también me deshiciste en una simplificación excesiva de mi pregunta. Técnicamente no estoy usando LinqToSql, estoy usando DbLinq (que se supone que es "como LinqToSql") y una base de datos SQLite, y cuando ejecuto su código, no arroja ningún error. Entonces, ahora soy de la opinión de que Dispose() simplemente no está implementado en DbLinq (o no se implementó correctamente). En cualquier caso, parece que necesito realizar todas las operaciones que se necesitarán para un 'DataContext' * en particular dentro del bloque' using'. – devuxer

+0

@DanM: Interesante de hecho. Supongo que es 'LIKE' Linq para SQL en el sentido literal de SQL de la palabra. :-) Sin embargo, el punto # 3 es probablemente el más importante y aún válido; Llamar a 'Dispose' nunca puede perjudicarlo (bueno, excepto con WCF), pero * no * lo llama con frecuencia * will *. Dado que el código base de DbLinq todavía está en la versión 0.2 (es decir, no es "estable"), no me sorprendería si los cambios de implementación futuros rompen el caso de uso incorrecto pero que funciona aquí. – Aaronaught

+0

Jaja, el segundo punto más importante es que aprendí algo sobre la forma correcta de usar un objeto DataContext. Gracias por tu ayuda. – devuxer

0

"personas" es una colección IEnumerable, el DataContext (repositorio) solo es necesario para realizar la llamada .GetNew.

las palabras clave de/select/etc son azúcar sintáctica para los métodos de extensión agregados en el espacio de nombres System.Linq. Estos métodos de extensión agregan la funcionalidad IEnumerable que está utilizando en su consulta, no el DataContext. De hecho, puede hacer todo esto sin usar LINQ2SQL en absoluto, creando programáticamente un IEnumerable para demostrar.

Si intenta realizar más repositorios (DataContext) llama utilizando estos objetos, es cuando recibirá un error.

La colección IEnumerable contendrá TODOS los registros de su repositorio, es por eso que no necesita el DataContext para realizar la consulta.

Los métodos de extensión: http://msdn.microsoft.com/en-us/library/bb383977.aspx

métodos de extensión LINQ: http://msdn.microsoft.com/en-us/library/system.linq.enumerable_members.aspx

+0

Entiendo acerca de los métodos de extensión, y uso LINQ todo el tiempo (no solo LinqToSql), pero supongo que no entiendo exactamente qué hace DataContext. El hecho de que le pase un objeto 'Conexión' me hace pensar que debe ser responsable de consultar la base de datos. Sin embargo, incluso si me deshago de él, las consultas aparentemente se ejecutan. ¿Cómo es eso posible? – devuxer

+0

El contexto de datos es creado de fábrica por Visual Studio para construir IEnumerables desde su base de datos. Sería mucho más fácil para usted utilizar realmente el DataContext en su consulta, en lugar de usar GetAll en un método diferente. – Sprague

+0

La consulta que está realizando no es una consulta en una base de datos, es una consulta en contra de una colección de objetos que obtuvo de la base de datos y almacenó en la memoria anteriormente (en su ejemplo). – Sprague

Cuestiones relacionadas