asp.net - practicas - Patrón de repositorio de Mejores Prácticas
patron repositorio net core (3)
Con respecto a 1: por lo que puedo ver, no es el propio IQuerable el problema que se devuelve desde un repositorio. El punto de un repositorio es que debe parecerse a un objeto que contiene todos sus datos. Así que puedes pedirle al repositorio los datos. Si tiene más de un objeto que necesita los mismos datos, la tarea del repositorio es almacenar en caché los datos, de modo que los dos clientes de su repositorio obtendrán las mismas instancias, de modo que si un cliente cambia una propiedad, el otro verá que , porque apuntan a la misma instancia.
Si el repositorio era en realidad el proveedor de Linq, entonces eso encajaría bien. Pero la mayoría de las personas simplemente permiten que IQuerable del proveedor de Linq-sql se transfiera, lo que, en efecto, elude la responsabilidad del repositorio. Entonces, el repositorio no es un repositorio en absoluto, al menos de acuerdo con mi comprensión y uso del patrón.
Con respecto a 2: Naturalmente, es más eficaz en el rendimiento simplemente devolver un solo valor de la base de datos que todo el registro. Pero al utilizar un patrón de repositorio, no devolvería ningún registro, sino que devolvería objetos de negocio. Por lo tanto, la lógica de la aplicación no debería ocuparse de los campos, sino de los objetos de dominio.
Pero, ¿qué tan efectivo es devolver un solo valor en comparación con un objeto de dominio completo? Probablemente no podrá medir la diferencia si su esquema de base de datos está razonablemente bien definido.
Es mucho más importante tener un código limpio y fácil de entender, en lugar de optimizaciones de rendimiento microscópicas en la delantera.
Así que estoy implementando el patrón de repositorio en una aplicación y encontré dos "problemas" en mi comprensión del patrón:
Consultas: he leído las respuestas que IQueryable no debe usarse cuando se usan repositorios. Sin embargo, es obvio que querrá que no devuelva una lista completa de objetos cada vez que llame a un método. ¿Debe ser implementado? Si tengo un método IEnumerable llamado Lista, ¿cuál es la "mejor práctica" general para un IQueryable? ¿Qué parámetros debería / no debería tener?
Valores escalares: ¿cuál es la mejor manera (usando el patrón del repositorio) para devolver un valor escalar único sin tener que devolver todo el registro? Desde el punto de vista del rendimiento, ¿no sería más eficiente devolver solo un valor escalar en una fila completa?
En respuesta a @lordinateur no me gusta mucho la manera concreta de especificar una interfaz de repositorio.
Debido a que la interfaz en su solución requiere que cada implementación del repositorio requiera al menos un Agregar, Eliminar, GetById, etc. Ahora considere un escenario en el que no tiene sentido guardar a través de una instancia particular de un repositorio, todavía tiene que implementar los métodos restantes con NotImplementedException o algo así.
Prefiero dividir las declaraciones de la interfaz de mi repositorio así:
interface ICanAdd<T>
{
T Add(T entity);
}
interface ICanRemove<T>
{
bool Remove(T entity);
}
interface ICanGetById<T>
{
T Get(int id);
}
Una implementación de repositorio particular para una entidad SomeClass podría parecerse a lo siguiente:
interface ISomeRepository
: ICanAdd<SomeClass>,
ICanRemove<SomeClass>
{
SomeClass Add(SomeClass entity);
bool Remove(SomeClass entity);
}
Demos un paso atrás y echemos un vistazo a por qué creo que esta es una mejor práctica que implementar todos los métodos CRUD en una interfaz genérica.
Algunos objetos tienen requisitos diferentes que otros. Un objeto de cliente no se puede eliminar, PurchaseOrder no se puede actualizar y solo se puede crear un objeto ShoppingCart. Cuando uno está usando la interfaz genérica de IRepository, esto obviamente causa problemas en la implementación.
Aquellos que implementan el anti-patrón a menudo implementarán su interfaz completa y luego emitirán excepciones para los métodos que no admiten. Aparte de estar en desacuerdo con numerosos principios de OO, esto rompe su esperanza de poder usar su abstracción IRepository de manera efectiva a menos que también comiencen a ponerle métodos para que los objetos dados sean o no compatibles y los implementen más.
Una solución común a este problema es pasar a interfaces más granulares como ICanDelete, ICanUpdate, ICanCreate, etc. Esto, a la vez que soluciona muchos de los problemas que surgieron en términos de los principios de OO, también reduce en gran medida la cantidad de reutilización del código. ser visto como la mayoría de las veces uno ya no podrá usar la instancia concreta del Repositorio.
A ninguno de nosotros nos gusta escribir el mismo código una y otra vez. Sin embargo, un contrato de depósito como una costura arquitectónica es el lugar equivocado para ampliar el contrato y hacerlo más genérico.
Estos extractos han sido tomados de esta publicación, donde también puedes leer más comentarios en los comentarios.
Estrictamente hablando, un repositorio ofrece una semántica de colección para obtener / poner objetos de dominio. Proporciona una abstracción en torno a su implementación de materialización (ORM, laminado a mano, simulado) para que los consumidores de los objetos de dominio se desacoplen de esos detalles. En la práctica, un Repositorio generalmente abstrae el acceso a las entidades, es decir, los objetos de dominio con identidad, y generalmente un ciclo de vida persistente (en el sabor DDD, un Repositorio proporciona acceso a las Raíces Agregadas).
Una interfaz mínima para un repositorio es la siguiente:
void Add(T entity);
void Remove(T entity);
T GetById(object id);
IEnumerable<T> Find(Specification spec);
Aunque verá las diferencias de nombre y la adición de la semántica Save / SaveOrUpdate, la idea anterior es pura. Obtienes los miembros de ICollection Agregar / Quitar más algunos buscadores. Si no usa IQueryable, también verá métodos de búsqueda en el repositorio como:
FindCustomersHavingOrders();
FindCustomersHavingPremiumStatus();
Hay dos problemas relacionados con el uso de IQueryable en este contexto. El primero es la posibilidad de filtrar detalles de implementación al cliente en la forma de las relaciones del objeto de dominio, es decir, violaciones de la Ley de Demeter. La segunda es que el repositorio adquiere responsabilidades de búsqueda que pueden no pertenecer al repositorio de objetos de dominio propiamente dicho, por ejemplo, encontrando proyecciones que son menos acerca del objeto de dominio solicitado que los datos relacionados.
Además, el uso de IQueryable ''rompe'' el patrón: un Repositorio con IQueryable puede o no proporcionar acceso a ''objetos de dominio''. IQueryable le brinda al cliente muchas opciones sobre lo que se materializará cuando finalmente se ejecute la consulta. Este es el objetivo principal del debate sobre el uso de IQueryable.
Con respecto a los valores escalares, no debe usar un repositorio para devolver valores escalares. Si necesita un escalar, normalmente lo obtendrá de la propia entidad. Si esto suena ineficiente, lo es, pero puede que no lo note, dependiendo de las características / requisitos de su carga. En los casos en que necesite vistas alternativas de un objeto de dominio, debido a razones de rendimiento o porque necesita combinar datos de muchos objetos de dominio, tiene dos opciones.
1) Use el repositorio de la entidad para encontrar las entidades especificadas y el proyecto / mapa para una vista plana.
2) Cree una interfaz de buscador dedicada a devolver un nuevo tipo de dominio que encapsule la vista plana que necesita. Esto no sería un Repositorio porque no habría una semántica de Colección, pero podría usar los repositorios existentes debajo de las cubiertas.
Una cosa a considerar si usa un Repositorio ''puro'' para acceder a las entidades persistentes es que compromete algunos de los beneficios de un ORM. En una implementación ''pura'', el cliente no puede proporcionar un contexto sobre cómo se usará el objeto de dominio, por lo que no puede decirle al repositorio: ''hey, solo voy a cambiar la propiedad customer.Name, por lo que no No te molestes en obtener esas referencias cargadas de entusiasmo. Por otro lado, la pregunta es si un cliente debe saber sobre esas cosas. Es una espada de doble filo.
En lo que respecta al uso de IQueryable, la mayoría de las personas parece estar cómoda con "romper" el patrón para obtener los beneficios de la composición dinámica de consultas, especialmente para las responsabilidades del cliente como la paginación / clasificación. En cuyo caso, usted podría tener:
Add(T entity);
Remove(T entity);
T GetById(object id);
IQueryable<T> Find();
y luego puede eliminar todos esos métodos de Finder personalizados, que realmente saturan el Repositorio a medida que crecen los requisitos de consulta.