¿Por qué no debería usar el patrón de repositorio con Entity Framework?

190

Durante una entrevista de trabajo, me pidieron que explicara por qué el patrón de repositorio no es un buen patrón para trabajar con ORM como Entity Framework. ¿Por qué es este el caso?

    
pregunta StringBuilder 12.12.2012 - 20:50

9 respuestas

93

No veo ninguna razón para que el patrón de Repository no funcione con Entity Framework. El patrón de repositorio es una capa de abstracción que coloca en su capa de acceso a datos. Su capa de acceso a datos puede ser cualquier cosa, desde procedimientos almacenados de ADO.NET puros hasta Entity Framework o un archivo XML.

En sistemas grandes, donde tiene datos provenientes de diferentes fuentes (base de datos / XML / servicio web), es bueno tener una capa de abstracción. El patrón del repositorio funciona bien en este escenario. No creo que Entity Framework sea suficiente abstracción para ocultar lo que sucede detrás de la escena.

He usado el patrón de Repository con Entity Framework como mi método de capa de acceso a datos y todavía tengo que enfrentar un problema.

Otra ventaja de abstraer el DbContext con un repositorio es la capacidad de prueba de la unidad . Puede tener su interfaz IRepository con 2 implementaciones, una (el Repositorio real) que usa DbContext para comunicarse con la base de datos y la segunda, FakeRepository que puede devolver objetos en memoria o datos simulados. Esto hace que su IRepository sea verificable por unidades, por lo tanto, otras partes del código que usan IRepository .

public interface IRepository
{
  IEnumerable<CustomerDto> GetCustomers();
}
public EFRepository : IRepository
{
  private YourDbContext db;
  private EFRepository()
  {
    db = new YourDbContext();
  }
  public IEnumerable<CustomerDto> GetCustomers()
  {
    return db.Customers.Select(f=>new CustomerDto { Id=f.Id, Name =f.Name}).ToList();
  }
}
public MockRepository : IRepository
{
  public IEnumerable<CustomerDto> GetCustomers()
  {
    // to do : return a mock list of Customers
    // Or you may even use a mocking framework like Moq
  }
}

Ahora usando DI, obtienes la implementación

public class SomeService
{
  IRepository repo;
  public SomeService(IRepository repo)
  {
     this.repo = repo;
  }  
  public void SomeMethod()
  {
    //use this.repo as needed
  }    
}
    
respondido por el Shyju 12.12.2012 - 22:02
400

¿El mejor motivo para no usar el patrón de repositorio con Entity Framework? Entity Framework ya implementa un patrón de repositorio. DbContext es su UoW (Unidad de trabajo) y cada DbSet es el repositorio. Implementar otra capa encima de esto no solo es redundante, sino que también dificulta el mantenimiento.

Las personas siguen patrones sin darse cuenta del propósito del patrón. En el caso del patrón de repositorio, el propósito es abstraer la lógica de consulta de la base de datos de bajo nivel. En los viejos tiempos de escribir declaraciones SQL en su código, el patrón del repositorio era una manera de sacar ese SQL de los métodos individuales dispersos en su base de código y localizarlo en un lugar. Tener un ORM como Entity Framework, NHibernate, etc. es un reemplazo para esta abstracción de código, y como tal, niega la necesidad del patrón.

Sin embargo, no es una mala idea crear una abstracción sobre tu ORM, simplemente no es algo tan complejo como UoW / repostitory. Me gustaría ir con un patrón de servicio, donde construyes una API que tu aplicación pueda usar sin saber o sin importar si los datos provienen de Entity Framework, NHibernate o una API web. Esto es mucho más simple, ya que simplemente agrega métodos a su clase de servicio para devolver los datos que necesita su aplicación. Si estaba escribiendo una aplicación de Tareas pendientes, por ejemplo, podría tener una llamada de servicio para devolver los elementos que vencen esta semana y aún no se han completado. Todo lo que su aplicación sabe es que si quiere esta información, llama a ese método. Dentro de ese método y en su servicio en general, interactúa con Entity Framework o cualquier otra cosa que esté usando. Luego, si más tarde decide cambiar los ORM o extraer la información de una API web, solo tendrá que cambiar el servicio y el resto de su código seguirá adelante, nadie lo sabe.

Puede parecer que es un argumento potencial para usar el patrón de repositorio, pero la diferencia clave aquí es que un servicio es una capa más delgada y está orientado a devolver datos totalmente horneados, en lugar de algo en lo que continúas consultando como con un repositorio.

    
respondido por el Chris Pratt 03.12.2013 - 03:23
42

Aquí hay una toma de Ayende Rahien: Arquitectos en el hoyo de la fatalidad: los males de la capa de abstracción del repositorio

Todavía no estoy seguro de si estoy de acuerdo con su conclusión. Es un catch-22: por un lado, si envuelvo mi Contexto EF en repositorios específicos para cada tipo con métodos de recuperación de datos específicos de la consulta, en realidad puedo realizar una prueba unitaria de mi código (algo así), lo cual es casi imposible con Entity Marco solo. Por otro lado, pierdo la capacidad de realizar consultas enriquecidas y el mantenimiento semántico de las relaciones (pero incluso cuando tengo acceso completo a esas funciones, siempre siento que estoy caminando sobre cáscaras de huevo alrededor de EF o cualquier otro ORM que pueda elegir. , ya que nunca sé qué métodos podría o no admitir su implementación de IQueryable, si interpretará mi adición a una colección de propiedades de navegación como una creación o simplemente una asociación, si será una carga perezosa o ansiosa o no se cargará en absoluto por por defecto, etc., así que tal vez sea para mejor. El "mapeo" relacional de objetos de impedancia cero es algo de una criatura mitológica, tal vez es por eso que la última versión de Entity Framework se denominó "Magic Unicorn".

Sin embargo, recuperar sus entidades a través de métodos de recuperación de datos específicos de la consulta significa que sus pruebas unitarias ahora son esencialmente pruebas de caja blanca y no tiene otra opción en este asunto, ya que debe saber de antemano exactamente qué método de repositorio es la unidad bajo prueba. va a llamar para burlarse. Y todavía no está probando las consultas, a menos que también escriba pruebas de integración.

Estos son problemas complejos que necesitan una solución compleja. No puede arreglarlo simplemente simulando que todas sus entidades son tipos separados sin relaciones entre ellas y atomice cada una en su propio repositorio. Bueno, tú puedes , pero apesta.

Actualización: He tenido cierto éxito al utilizar el proveedor de Esfuerzo para Entity Framework. Effort es un proveedor en memoria (código abierto) que le permite usar EF en las pruebas exactamente de la misma forma en que lo usaría contra una base de datos real. Estoy considerando cambiar todas las pruebas en este proyecto. Estoy trabajando para usar este proveedor, ya que parece facilitar mucho las cosas. Es la única solución que he encontrado hasta ahora que aborda todos los problemas sobre los que estaba hablando antes. Lo único es que hay un ligero retraso al iniciar mis pruebas, ya que está creando la base de datos en memoria (usa otro paquete llamado NMemory para hacer esto), pero no veo esto como un problema real. Hay un artículo de Code Project que habla sobre el uso de Esfuerzo (versus SQL CE) para pruebas.

    
respondido por el luksan 31.12.2012 - 19:17
16

La razón por la que probablemente lo harías es porque es un poco redundante. Entity Framework le brinda una gran cantidad de ventajas de codificación y funcionales, es por eso que lo usa, si luego lo toma y lo envuelve en un patrón de repositorio que está desechando, puede que también esté utilizando cualquier otra capa de acceso a datos. / p>     

respondido por el Nine Tails 20.03.2013 - 18:05
14

En teoría, creo que tiene sentido encapsular la lógica de conexión de la base de datos para hacerla más fácilmente reutilizable, pero como argumenta el enlace a continuación, nuestros marcos modernos se encargan de esto ahora.

Reconsiderando el patrón del repositorio

    
respondido por el Splendor 12.12.2012 - 21:32
6

Una muy buena razón para utilizar el patrón de repositorio es permitir la separación de su lógica empresarial y / o su UI de System.Data.Entity. Hay numerosas ventajas en esto, que incluyen beneficios reales en las pruebas unitarias al permitir el uso de falsificaciones o simulacros.

    
respondido por el James Culshaw 20.06.2013 - 17:41
0

Hemos tenido problemas con instancias duplicadas pero diferentes de Entity Framework DbContext cuando un contenedor IoC que almacena nuevos () depósitos por tipo (por ejemplo, un UserRepository y una instancia de GroupRepository que cada uno llama a su propio IDbSet desde DBContext), a veces puede causar múltiples contextos por solicitud (en un contexto MVC / web).

La mayoría de las veces todavía funciona, pero cuando agrega una capa de servicio encima de eso y esos servicios suponen que los objetos creados con un contexto se adjuntarán correctamente como colecciones secundarias a un objeto nuevo en otro contexto, a veces falla y A veces no depende de la velocidad de los compromisos.

    
respondido por el Mufasa 04.02.2015 - 21:38
-1

Después de probar el patrón de repositorio en un proyecto pequeño, recomiendo encarecidamente que no lo use; ¡No porque complique su sistema, y no porque los datos de burla sean una pesadilla, sino porque sus pruebas se vuelven inútiles!

Los datos de simulación le permiten agregar detalles sin encabezados, agregar registros que violan las restricciones de la base de datos y eliminar entidades que la base de datos rechazaría eliminar. En el mundo real, una única actualización puede afectar a varias tablas, registros, historial, resúmenes, etc., así como a columnas como el último campo de fecha de modificación, las claves generadas automáticamente, los campos calculados.

En resumen, ejecutar su prueba en una base de datos real le proporciona resultados reales y puede probar no solo sus servicios e interfaces, sino también el comportamiento de la base de datos. Puede verificar si sus procedimientos almacenados hacen lo correcto con los datos, devuelven el resultado esperado o si el registro que envió para eliminar realmente se eliminó. Dichas pruebas también pueden exponer problemas tales como olvidarse de generar errores en el procedimiento almacenado y miles de estos escenarios.

Creo que el marco de la entidad implementa un patrón de repositorio mejor que cualquiera de los artículos que he leído hasta ahora y va mucho más allá de lo que están tratando de lograr.

El repositorio era la mejor práctica en aquellos días en que usábamos XBase, AdoX y Ado.Net, ¡pero con entidad! (Repositorio sobre repositorio)

Por último, creo que mucha gente invierte mucho tiempo en aprender e implementar patrones de repositorio y se niegan a dejarlo pasar. Principalmente para demostrarse a sí mismos que no perdieron el tiempo.

    
respondido por el Zawer Haroon 15.10.2015 - 04:40
-3

Se debe a migraciones: no es posible hacer que funcionen las migraciones, ya que la cadena de conexión reside en web.config. Pero, el DbContext reside en la capa del repositorio. IDbContextFactory necesita tener una cadena de configuración para la base de datos. Pero no hay forma de que las migraciones obtengan la cadena de conexión de web.config.

Hay soluciones, pero aún no he encontrado una solución limpia para esto.

    
respondido por el Thunder 18.12.2015 - 06:16

Lea otras preguntas en las etiquetas