for - ¿Necesitamos usar el patrón de Repository cuando trabajamos en ASP.NET MVC con soluciones ORM?
input asp for (3)
Tengo un poco de curiosidad por saber qué experiencia tienen otros desarrolladores de aplicar el patrón de Repository al programar en ASP.NET MVC con Entity Framework o NHibernate. Me parece que este patrón ya está implementado en el ORM en sí. DbContext
y DbSet<T>
en el Entity Framework y por el ISession
en NHibernate. La mayoría de las inquietudes mencionadas en el patrón de Repository
, tal como están catalogadas en POEE y DDD , están implementadas de manera bastante adecuada por estos ORM. A saber, estas preocupaciones son,
- Persistencia
- OO Vista de los datos.
- Abstracción lógica de acceso a datos
- Lógica de acceso a consultas
Además, la mayoría de las implementaciones del patrón de repositorio que he visto siguen este patrón de implementación, asumiendo que estamos desarrollando una aplicación de blog.
Implementación de NHibernate:
public class PostRepository : IPostRepository
{
private ISession _session;
public PostRepository(ISession session)
{
_session = session;
}
public void Add(Post post)
{
_session.Save(post);
}
// other crud methods.
}
Marco de la entidad:
public class PostRepository : IPostRepository
{
private DbContext _session;
public PostRepository(DbContext session)
{
_session = session;
}
public void Add(Post post)
{
_session.Posts.Add(post);
-session.SaveChanges();
}
// other crud methods.
}
Me parece que cuando usamos ORM, como Nhibernate o Entity Framework, la creación de estos repositorios de implementación es redundante. Además, dado que estas implementaciones de patrones no hacen más que lo que ya existe en la ORMS, estas actúan más como ruido que como abstracción de OO. Parece que el uso del patrón de repositorio en la situación mencionada anteriormente no es más que un engrandecimiento del desarrollador y más pompa y ceremonia sin ningún beneficio técnico realizable. Cuáles son tus pensamientos ??
Creo que tiene sentido solo si quieres disminuir el nivel de dependencia. En el resumen, puede tener IPostRepository en su paquete de infraestructura y varias implementaciones independientes de esta interfaz construida sobre EF o NH, o algo más. Es útil para TDD.
En la práctica, la sesión NH (y el contexto EF) implementa algo como el patrón de "Unidad de trabajo". Además, con NH y el patrón del Repositorio, puede obtener muchos errores y problemas de arquitectura.
Por ejemplo, la entidad NH puede guardarse sin pasar por la implementación del Repositorio. Puede obtenerlo desde la sesión (Repository.Load), cambiar una de sus propiedades y llamar a session.Flush (al final de la solicitud, por ejemplo, porque el patrón del Repository no supone el vaciado) - y sus cambios se procesarán con éxito db
La respuesta es no si no necesita poder cambiar de ORM o probar cualquier clase que tenga una dependencia de su ORM / base de datos.
Si desea poder cambiar ORM o poder probar fácilmente sus clases que utilizan la capa de base de datos: Sí, necesita un repositorio (con una especificación de interfaz).
También puede cambiar a un repositorio de memoria (que hago en mis pruebas de unidad), a un archivo XML o lo que sea si usa un patrón de repositorio.
Actualizar
El problema con la mayoría de las implementaciones de patrones de repositorio que puede encontrar en Google es que no funcionan muy bien en producción. Carecen de opciones para limitar el resultado (paginación) y ordenar el resultado, lo cual es sorprendente.
El patrón de repositorio llega a su gloria cuando se combina con una implementación de UnitOfWork y tiene soporte para el patrón de especificación.
Si encuentra que uno tiene todo eso, avíseme :) (Tengo mi propia excepción, para una parte de especificación que funciona bien)
Actualización 2
El repositorio es mucho más que simplemente acceder a la base de datos de una manera abstracta, como puede ser hecho por ORM. Una implementación normal del repositorio debe manejar todas las entidades agregadas (por ejemplo, Order
y OrderLine
). Si los maneja en la misma clase de repositorio, siempre puede asegurarse de que estén construidos correctamente.
Pero oye, tú dices: eso lo hace automáticamente el ORM. Pues sí y no. Si crea un sitio web, lo más probable es que desee editar solo una línea de pedido. ¿Obtienes el pedido completo, lo recorres para encontrarlo y luego lo agregas a la vista?
Al hacerlo, introduces lógica a tu controlador que no pertenece a él. ¿Cómo lo haces cuando un servicio web quiere lo mismo? ¿Duplicar su código?
Mediante el uso de un ORM es bastante fácil obtener cualquier entidad desde cualquier lugar myOrm.Fetch<User>(user => user.Id == 1)
modificarlo y luego guardarlo. Esto puede ser muy útil, pero también agrega olores de código ya que duplica el código y no tiene control sobre cómo se crean los objetos, si obtuvieron un estado válido o asociaciones correctas.
Lo siguiente que viene a la mente es que es posible que desee suscribirse a eventos como Creado, Actualizado y Borrado de una manera centralizada. Eso es fácil si tienes un repositorio.
Para mí, un ORM proporciona una manera de asignar clases a tablas y nada más. Todavía me gusta envolverlos en repositorios para tener control sobre ellos y obtener un único punto de modificación.
Solo has mencionado las acciones básicas de CRUD. Hacer esto directamente significa que debe estar al tanto de las transacciones, el vaciado y otras cosas que un repositorio puede envolver, pero creo que el valor de los repositorios se hace más evidente cuando piensa en consultas de recuperación complejas.
Imagina entonces que decides utilizar la sesión de NHibernate directamente en tu capa de aplicación.
Tendrá que hacer el equivalente de las cláusulas WHERE y ORDER BY, etc., utilizando los criterios de HQL o NHibernate. Esto significa que su código tiene que hacer referencia a NHibernate y contiene ideas específicas de NHibernate. Esto hace que su aplicación sea difícil de probar y más difícil de seguir para otras personas no familiarizadas con NH. Una llamada a repository.GetCompletedOrders es mucho más descriptiva y reutilizable que una que incluye algo como "donde IsComplete = true y IsDeleted = false ..." etc.
Podría usar Linq para NHibernate en su lugar, pero ahora tiene la situación en la que puede olvidar fácilmente que está trabajando en un IQueryable. ¡Podría terminar encadenando expresiones Linq que generan enormes consultas cuando se ejecutan, sin darse cuenta (hablo por experiencia)! Mike Hadlow provocó una conversación sobre este tema en su publicación Debería mi repositorio exponer a IQueryable .
Nb Si no le gusta tener muchos métodos en repositorios personalizados para diferentes consultas (como GetCompletedOrders), puede usar parámetros de especificación (como Get (especificación)), que le permiten especificar filtros, pedidos, etc. sin usar el idioma de acceso a datos .
Volviendo a la lista de beneficios del repositorio que dio:
- Persistencia
- OO Vista de los datos.
- Abstracción lógica de acceso a datos
- Lógica de acceso a consultas
Puede ver que los puntos 3 y 4 no se proporcionan utilizando las clases del marco de persistencia directamente, especialmente en escenarios de recuperación del mundo real.