practices - asp net c# visual studio 2013 tutorial
IEnumerable vs IQueryable para Business Logic o DAL return Types (1)
Sé que estas preguntas se han hecho antes, comenzaré por enumerar algunas (las que he leído hasta ahora):
- IEnumerable vs IQueryable
- List, IList, IEnumerable, IQueryable, ICollection, ¿cuál es el tipo de devolución más flexible?
- Devolver IEnumerable <T> frente a IQueryable <T>
- IEnumerable <T> como tipo de retorno
- https://stackoverflow.com/questions/2712253/ienumerable-and-iqueryable
- Vistas con lógica de negocios vs código
- WPF IEnumerable <T> versus IQueryable <T> como DataSource
- IEnumerable <T> VS IList <T> VS IQueryable <T>
- ¿Qué interfaz debería devolver mi servicio? IQueryable, IList, IEnumerable?
- ¿Debo devolver IEnumerable <T> o IQueryable <T> de mi DAL?
Como puede ver, hay algunos recursos geniales en SO solo sobre el tema, pero hay una pregunta / sección de la pregunta que aún no estoy seguro de haber leído todo esto.
Me preocupa principalmente la pregunta IEnumerable vs IQueryable, y más específicamente el acoplamiento entre DAL y sus consumidores .
He encontrado diversas opiniones sugeridas con respecto a las dos interfaces, que han sido geniales. Sin embargo, me preocupan las implicaciones de que un DAL devuelva IQueryable. Según tengo entendido, IQueryable sugiere / implica que hay un proveedor de Linq bajo el capó. Esa es la preocupación número uno: ¿qué ocurre si el DAL repentinamente requiere datos de una fuente que no es de Linq? Lo siguiente funcionaría, pero ¿es más un hack?
public static IQueryable<Product> GetAll()
{
// this function used to use a L2S context or similar to return data
// from a database, however, now it uses a non linq provider
// simulate the non linq provider...
List<Product> results = new List<Product> { new Product() };
return results.AsQueryable();
}
Entonces, ¿puedo usar la extensión AsQueryable () aunque no admito saber exactamente lo que hace? Siempre me imagino IQueryables como los árboles de expresión subyacentes que podemos agregar como sea necesario hasta que estemos listos para realizar nuestra consulta y obtener los resultados.
Podría rectificar esto cambiando el tipo de devolución de la función a IEnumerable. Luego puedo devolver IQueryable de la función porque hereda IEnumerable y puedo mantener la carga diferida. Lo que pierdo es la capacidad de agregar a la expresión de consulta:
var results = SomeClass.GetAll().Where(x => x.ProductTypeId == 5);
Al devolver IQueryable, tal como lo entiendo, esto simplemente agregaría la expresión. Al devolver IEnumerable, a pesar de mantener la carga diferida, la expresión debe evaluarse para que los resultados se lleven a la memoria y se enumeren para filtrar ProductTypeIds incorrectos.
¿Cómo se las arreglan las demás personas?
- Proporcione más funciones en DAL - GetAllByProductType, GetAllByStartDate, ... etc.
Proporcionar una sobrecarga que acepte predicados? es decir
public static IEnumerable<Product> GetAll(Predicate<Product> predicate) { List<Product> results = new List<Product> { new Product() }; return results.Where(x => predicate(x)); }
Una última parte (lo siento, lo sé, ¡pregunta realmente larga!).
Encontré que IEnumerable es el más recomendado en todas las preguntas que verifiqué, pero ¿qué pasa con el requisito de cargas diferidas para que un contexto de datos esté disponible? Según tengo entendido, si su función devuelve IEnumerable, pero usted devuelve IQueryable, IQueryable depende de un contexto de datos subyacente. Como el resultado en esta etapa es en realidad una expresión y no se ha llevado nada a la memoria, no se puede garantizar que el consumidor del DAL / función realizará la consulta, ni cuándo. Entonces, ¿tengo que mantener de alguna manera la instancia del contexto de la que se obtuvieron los resultados? ¿Es así como / por qué el patrón de Unidad de Trabajo entra en juego?
Resumen de las preguntas para mayor claridad (hizo una búsqueda de "?" ...):
- Si usa IQueryable como tipo de devolución, ¿está acoplando demasiado su UI / Business Logic con los proveedores de Linq?
- ¿Es una buena idea usar la extensión AsQueryable () si de repente necesita devolver datos de una fuente que no es Linq?
- Alguien tiene un buen enlace que describe cómo, por ejemplo, convertir una lista estándar en trabajos AsQueryable, ¿qué es lo que realmente hace?
- ¿Cómo se manejan los requisitos adicionales de filtrado suministrados por la lógica comercial a su DAL?
- Parece que la carga diferida tanto de IEnumerable como de IQueryable está sujeta al mantenimiento del proveedor subyacente, ¿debería utilizar un patrón de unidad de trabajo o alguna otra cosa para manejar esto?
¡Muchas gracias por adelantado!
- bueno, no estás estrictamente vinculado a ningún proveedor específico, sino como una nueva redacción de eso: no puedes probar fácilmente el código, ya que cada proveedor tiene diferentes funciones compatibles (es decir: lo que funciona para uno podría no funcionar para otro) - incluso algo así como
.Single()
) - No lo creo, si hay alguna pregunta en su mente sobre un proveedor en constante cambio - vea arriba
- solo proporciona un contenedor decorado que usa
.Compile()
en cualquier lambda, y usa LINQ-to-Objects en su lugar. Tenga en cuenta que LINQ-to-Objects tiene más soporte que cualquier otro proveedor, por lo que esto no será un problema, excepto que significa que cualquier "burla" que use este enfoque realmente no prueba su código real y en gran medida no tiene sentido ( IMO) - sí, complicado - ver abajo
- sí, complicado - ver abajo
Personalmente, preferiría API bien definidas que tomen los parámetros conocidos y devuelvan una List<T>
cargada List<T>
o IList<T>
(o similar) de resultados; esto le proporciona una API comprobable / simulada, y no lo deja a merced de la ejecución diferida (infierno de conexión cerrada, etc.). También significa que cualquier diferencia entre proveedores se maneja internamente para la implementación de su capa de datos. También se adapta mucho mejor a escenarios de llamada como servicios web, etc.
En breve; si IEnumerable<T>
una opción entre IEnumerable<T>
e IQueryable<T>
, no elijo ninguna opción, prefiero utilizar IList<T>
o List<T>
. Si necesito un filtrado adicional, entonces:
- Agregaré eso a la API existente a través de parámetros y haré el filtrado dentro de mi capa de datos
- Aceptaré que vuelvan los datos sobredimensionados, que luego debo filtrar a la persona que llama