Devolver un IQueryable desde un IRepository

8

Usando el patrón Repository, ¿es apropiado devolver un IQueryable de un conjunto de datos (tabla), para uso genérico?

Es muy útil en muchos casos, especialmente cuando se usan bibliotecas externas que aprovechan esa interfaz, por ejemplo, algunos complementos que ordenan / filtran y se unen a elementos ui.

Sin embargo, exponer un IQueryable a veces parece dejar el diseño propenso a errores. Esto, junto con un uso incorrecto de la carga diferida podría provocar graves problemas de rendimiento.

Por otro lado, tener métodos de acceso para cada uso parece redundante y también requiere mucho trabajo (considerando pruebas unitarias, etc.).

Mihalis Bagos
fuente
2
OMI: Ocultar la tecnología de persistencia (EF, NHibernate) de los desarrolladores es una mala idea. Simplemente exponga lo que expone esta tecnología.
Eufórico
@Euphoric Ok, pero si en ASP.NET MVC tengo controladores con 3000 líneas donde las personas crean felizmente new DbContext()cuando lo necesitan y llaman SaveChanges()cuando quieren. No es posible probar nada y solo puedo pensar en una capa adicional que lo oculta.
Mateusz
@Mateusz Eso no es un problema de tecnología, sino de personas. Usted tiene desarrolladores deficientes o una administración deficiente, que no puede capacitar adecuadamente a dichos desarrolladores.
Eufórico

Respuestas:

12

Mark Seemann tiene una excelente publicación de blog sobre este tema: IQueryable is Tight Coupling . Lo resume muy bien en la parte final (énfasis mío):

Puede pensar que todo esto es un ejercicio teórico, pero en realidad sí importa. Al escribir Clean Code, es importante diseñar una API de tal manera que quede claro lo que hace.

Una interfaz como esta ofrece falsas garantías:

public interface IRepository
{
    IQueryable<T> Query<T>();
}

De acuerdo con el LSP y la ley de Postel, parecería garantizar que puede escribir cualquier expresión de consulta (no importa cuán compleja) contra la instancia devuelta, y siempre funcionaría .

En la práctica, esto nunca va a suceder.

Los programadores que definen tales interfaces tienen invariablemente un ORM específico en mente , y tienden implícitamente a mantenerse dentro de los límites que saben que son seguros para ese ORM específico. Esta es una abstracción permeable.

Si tiene en mente un ORM específico, sea explícito al respecto. No lo escondas detrás de una interfaz. Crea la ilusión de que puede reemplazar una implementación con otra. En la práctica, eso es imposible. Imagine que intenta proporcionar una implementación en una tienda de eventos.

El pastel es una mentira.

ORM's como Entity Framework son implementaciones del repositorio y el patrón de unidad de trabajo. No hay necesidad de envolverlos en otro.

Kristof Claes
fuente
Gracias por la respuesta. Parece que mis preocupaciones se encuentran con las de Seemann muchas veces, pero en realidad no me he tomado el tiempo de leerlo, lo cual es mi intención. Lo único con lo que no estoy de acuerdo es que un repositorio, incluso si está fuertemente acoplado, es muy útil para encapsular la lógica empresarial, incluso si está en un nivel de acceso más simple (es decir: VisibleItems, DeletedItems), etc., y ayuda con el concepto DRY.
Mihalis Bagos
@ Kristof Solo una pregunta para "Entity Framework son implementaciones del Repositorio y el patrón de Unidad de Trabajo". ¿Qué puedo hacer para que la gente no ponga el new DbContext()controlador y la lógica compleja en un solo método en MVC y luego no ponga también algo de lógica en las vistas? Tengo un código heredado donde no puedo hacer ninguna prueba unitaria. Para habilitar las pruebas y evitar que las personas hagan códigos realmente malos, necesito una capa adicional. Por lo tanto, necesito implementar mis propios repositorios con fines educativos.
Mateusz,
Hay otras opciones como clases de servicio o clases de comando y consulta.
Kristof Claes
1

No habrá consenso sobre esto. En mi opinión y experiencia, un repositorio debería devolver objetos con usos específicos. Como mínimo, si utiliza el patrón de repositorio como lo define Eric Evens en DDD. Un repositorio es un "puente" que conecta la lógica empresarial, la persistencia y las fábricas.

Si desea acceder a la persistencia más directamente, tal vez esté buscando el Patrón de puerta de enlace.

Sin embargo, por lo que dices aquí, te gustaría ocultar esa exposición a la persistencia para que el Patrón de Proxy te sea útil.

Patkos Csaba
fuente
Si bien la exposición a la persistencia no es una preocupación principal, al menos una abstracción de nivel superior para patrones como entidades padre-hijo es obligatoria. Examinaré esos patrones también, ¡gracias!
Mihalis Bagos