2010-06-14 13 views
37

Sé que esto podría ser opinión, pero estoy buscando las mejores prácticas.¿Debo devolver IEnumerable <T> o IQueryable <T> de mi DAL?

Según entiendo, IQueryable<T> implementa IEnumerable<T>, así que en mi DAL, que actualmente tienen firmas de métodos como la siguiente:

IEnumerable<Product> GetProducts(); 
IEnumerable<Product> GetProductsByCategory(int cateogoryId); 
Product GetProduct(int productId); 

debo utilizar IQueryable<T> aquí?

¿Cuáles son los pros y los contras de ambos enfoques?

Nota que estoy planeando sobre el uso del patrón de depósito, así que tendrá una clase de esta manera:

public class ProductRepository { 

    DBDataContext db = new DBDataContext(<!-- connection string -->); 

    public IEnumerable<Product> GetProductsNew(int daysOld) { 
     return db.GetProducts() 
      .Where(p => p.AddedDateTime > DateTime.Now.AddDays(-daysOld)); 
    } 
} 

¿Debo cambiar mi IEnumerable<T>-IQueryable<T>? ¿Qué ventajas/desventajas hay para uno u otro?

Respuesta

49

Depende de lo que el comportamiento que desea.

  • Devolución de un IList <T> cuenta la persona que llama que han recibido todos los datos que han solicitado
  • Devolución de un IEnumerable <T> dice que la persona que llama que necesitan para iterar sobre el resultado y puede estar cargado de forma perezosa.
  • Devolución de un IQueryable <T> cuenta la persona que llama que el resultado está respaldado por un proveedor de LINQ que puede manejar ciertas clases de consultas, poniendo la carga sobre la persona que llama para formar una consulta performant.

Si bien este último le da a quien llama mucha flexibilidad (suponiendo que su repositorio lo admita por completo), es el más difícil de probar y, podría decirse que el menos determinista.

+0

Bien dicho. Breve conciso, con viñetas. – Armstrongest

+0

Derecho encendido. Siempre he agrupado a IList e IEnumerable en el mismo cubo, pero me gusta más esta idea. – scottm

4

HUUUUGGGE difference. Veo esto bastante.

Crea un IQueryable antes de que llegue a la base de datos. IQueryable solo llega al DB una vez que se llama una función ansiosa (.ToList() por ejemplo) o si realmente intenta extraer valores. IQueryable = flojo.

Un IEnumerable ejecutará su lambda contra la base de datos de inmediato. IEnumerable = ansioso.

En cuanto a qué usar con el patrón Repositorio, creo que está ansioso. Por lo general, veo que se pasan ILists, pero alguien más tendrá que resolver eso por ti. EDITAR - Usualmente ves IEnumerable en lugar de IQueryable porque no quieres capas más allá de tu Repositorio A) determinando cuándo sucederá la base de datos o B) Añadiendo lógica a las combinaciones fuera del Repositorio

Hay un LINQ muy bueno Es un video que disfruto mucho. No solo alcanza IEnumerable v IQueryable, sino que realmente tiene una visión fantástica.

http://channel9.msdn.com/posts/matthijs/LINQ-Tips-Tricks-and-Optimizations-by-Scott-Allen/

+0

Para que quede claro, 'ProductRepository' está dentro de mi aplicación ... mientras que DAL es un proyecto separado al que me refiero. Así que llamo al Repositorio desde mi aplicación, el Repositorio llama a la Clase DBDataContext en el Proyecto DAL (que contiene todos los métodos genéricos), que luego hace cualquier LinqToSQL contra la base de datos. – Armstrongest

+3

IEnumerable no es necesariamente ansioso. Enumerable.Select devuelve un IEnumerable perezoso. –

+0

Gran video. ¡Gracias por eso! – Armstrongest

3

Puede usar IQueryable y aceptar que alguien podría crear un escenario donde podría ocurrir SELECT N+1. Esto es una desventaja, junto con el hecho de que puede terminar con un código que es específico para la implementación de su repositorio en las capas superiores a su repositorio. La ventaja de esto es que permite que las operaciones comunes de delegación, como la búsqueda y la clasificación, se expresen fuera de su repositorio, lo que lo alivia de tales preocupaciones. También es más flexible si necesita unir los datos con otras tablas de la base de datos, ya que la consulta seguirá siendo una expresión, por lo que puede agregarse antes de que se resuelva en una consulta y llegue a la base de datos.

La alternativa es bloquear su repositorio para que devuelva listas materializadas llamando al ToList(). Con el ejemplo de paginación y clasificación, deberá pasar skip, take y una expresión de ordenación como parámetros a los métodos de su repositorio, y usar los parámetros para devolver solo una ventana de resultados. Esto significa que el repositorio asume la responsabilidad de buscar y clasificar, y toda la proyección de sus datos.

Esto es una especie de decisión, le da a su aplicación el poder de linq, y tiene menos complejidad en el repositorio, o controla el acceso a sus datos. Para mí, depende del número de consultas asociadas con cada entidad y de las combinaciones de entidades, y de dónde quiero gestionar esa complejidad.

+0

Hmmm ... algunas cosas en que pensar. – Armstrongest

6

Una cosa más a tener en cuenta: ¿dónde está tu soporte de paginación/clasificación? Si proporciona soporte de paginación dentro de su repositorio, devolver IEnumerable<T> está bien. Si está buscando fuera de su repositorio (como en el controlador o la capa de servicio), entonces realmente desea utilizar IQueryable<T> porque no desea cargar todo el conjunto de datos en la memoria antes de que se pagine.

Cuestiones relacionadas