work what unit pattern patron framework diseƱo c# repository-pattern iqueryable objectquery

what - unit of work interface c#



Repositorio/IQueryable/Objeto de consulta (2)

Estoy construyendo un repositorio y he visto en muchos lugares 2 razones para no exponer IQueryable fuera del repositorio.

1) La primera es porque diferentes proveedores de LINQ podrían comportarse de manera diferente, y esta diferencia debe estar contenida dentro del repositorio.

2) El segundo es evitar que los desarrolladores de nivel de servicio modifiquen la consulta de la base de datos de manera que accidentalmente cause problemas de rendimiento.

¿Supongo que el problema 2 solo se puede evitar manteniendo toda la lógica de consulta dentro del repositorio y no permitiendo ninguna forma de creación de consultas externas? Pero eso me parece poco práctico.

El problema 1 parece resolverse utilizando el patrón de objeto de datos.

p.ej. public IEnumerable<T> FindBy(Query query)

Mi pregunta es, ¿por qué no solo pasaría una expresión lambda, ya que es independiente del proveedor, parece que me proporciona la misma funcionalidad que un objeto de consulta y el mismo nivel de separación?

por ejemplo public IEnumerable<T> FindBy(Expression<Func<T,bool>> predicate)

¿Hay alguna razón para no hacer esto? ¿Se rompen algunas reglas? ¿Mejores prácticas? que debo saber?


Solo devuelve un IQueryable<T> .

Antes de escribir otro fragmento de "código de repositorio", se beneficiará significativamente al leer el artículo de Ayende Arquitectos en el hoyo de la fatalidad - Los males de la capa de abstracción del repositorio

Su enfoque es, sin duda, añadiendo una complejidad innecesaria significativa.

Todo el código de la otra pregunta en la Lista genérica de OrderBy Lambda no hace nada más que enmascarar una API efectiva existente con una abstracción innecesaria y desconocida.

En cuanto a sus dos preocupaciones,

  1. Los proveedores de LINQ se comportan de manera diferente, pero siempre que el proveedor de LINQ pueda procesar los predicados que usted está pasando, esto es irrelevante. De lo contrario, seguirá teniendo el mismo problema, porque está pasando una Expression , que de todos modos pasa a IQueryable . Si la implementación de IQueryProvider no puede manejar su predicado, entonces no puede manejar su predicado. (Siempre puede llamar a un ToList() si necesita realizar una evaluación antes de seguir con el filtrado y no se puede traducir).

  2. La modificación de una consulta puede causar problemas de rendimiento, pero es más probable que exponga una funcionalidad muy necesaria. Además, es probable que los problemas de rendimiento incurridos por una consulta LINQ subóptima sean significativamente menos perjudiciales que los problemas de rendimiento incurridos al extraer muchos más registros de los necesarios para evitar exponer un IQueryable o filtrar sistemáticamente cualquier lógica de acceso a datos a través de niveles de abstracciones hinchados que en realidad no hacen nada (la primera amenaza es más significativa). En general, esto no será un problema porque la mayoría de los proveedores líderes de LINQ optimizarán su lógica de consulta en el proceso de traducción.

Si desea ocultar su lógica de consulta desde el extremo frontal, no intente crear un repositorio genérico. Encapsule las consultas con métodos específicos de negocios reales. Ahora, puedo estar equivocado, pero supongo que su uso del patrón de repositorio está inspirado en Domain Driven Design. Si este es el caso, entonces la razón para usar un repositorio es permitirte crear un dominio ignorante de persistencia con un enfoque principal en el modelo de dominio. Sin embargo, el uso de este tipo de repositorio genérico no hace mucho más que cambiar su semántica de Create Read Update Delete para Find Add Remove Save . No hay ningún conocimiento real de negocios incrustado allí.

Considere el significado (y usabilidad) de un

interface IPersonRepository { Person GetById(int id); IEnumerable<Person> FindByName(string firstName, string lastName); }

en contraste con

interface IRepository<T> { IEnumerable<T> FindBy(Query<T> query); }

Además, ¿puede realmente señalar un beneficio para usar el IRepository<T> (en lugar de un IQueryable<T> )?

Además, tenga en cuenta que con el enfoque genérico, en realidad no está encapsulando la lógica de consulta. Terminas construyéndolo externamente, lo que conducirá a más código innecesario adicional.

* Otra nota sobre los recursos que desaconsejan el uso de IQueryable<T> , es que vale la pena mirar su fecha de publicación. Hubo un momento en que la disponibilidad de los proveedores de LINQ era bastante limitada (a principios de EF y LINQ-to-SQL). En ese momento, la exposición de un IQueryable<T> conllevaría una incompatibilidad con algunos de los sustitutos más populares de Microsoft ORM (desde hace mucho tiempo que se implementó LINQ-to-NHibernate). En este momento, el soporte de LINQ es prácticamente omnipresente en las bibliotecas ORM .NET serias


En contraste con la respuesta anterior, un punto claro de oposición sería la prueba de unidad . Devolver un IQueryable genérico simple es una abstracción permeable a cualquier cliente que tenga acceso para llamarlo. No hay restricción para el método del repositorio, por lo tanto, no hay nada que afirmar en una prueba de unidad.

El uso de IQueryable casi seguro que depende del contexto de la aplicación en sí y de la arquitectura. Por ejemplo, si está trabajando en un proyecto personal por su cuenta, puede asumir la responsabilidad de ejercer este poder ilimitado sin perjudicar a nadie, excepto a usted mismo. Sin embargo, si está trabajando en una aplicación para una compañía con código que entra en producción, ciertamente hay más partes interesadas de las que usted involucra. En este escenario, podría estar trabajando en la capa de acceso a datos e intentando codificar contra el uso de su código por parte de otros desarrolladores. Ahora necesita controlar lo que entra y sale para que no rompan su código y usted no rompa el suyo. Ese es el incentivo, y la prueba de unidad es la aplicación.

Dicho esto, no utilice simplemente IQueryable<T> . Usa lo que tenga sentido para tu aplicación.