c# entity-framework repository-pattern generic-repository

c# - ¿Repositorio genérico o repositorio específico para cada entidad?



entity-framework repository-pattern (1)

Fondo

En la empresa donde trabajo, se me ha ordenado actualizar una aplicación MVC antigua e implementar un patrón de repositorio para una base de datos SQL. He creado el contexto de la base de datos utilizando Entity Framework Database-First y obtuve 23 entidades.

La primera pregunta

¿Necesito crear un repositorio para cada entidad o implementar un repositorio genérico para el contexto? Te lo pregunto porque he encontrado seguidores mientras buscaba en internet:

Un repositorio por dominio.

Debe pensar en un repositorio como una colección de objetos de dominio en la memoria. Si está creando una aplicación llamada Vega, no debería tener un repositorio como el siguiente:

public class VegaRepository {}

Fuente: Programación con Mosh: 4 errores comunes con el patrón de repositorio

La segunda pregunta

¿Funciona un repositorio genérico para Entity Framework Database-First? Esto es porque he encontrado siguientes mientras buscaba en internet:

Marco de la entidad

Tenga en cuenta que el patrón de repositorio solo es útil si tiene POCO que se asignan utilizando el código primero. De lo contrario, simplemente romperá la abstracción con las entidades (= el patrón del repositorio no es muy útil entonces). Puede seguir este artículo si desea obtener una base generada para usted.

Fuente: CodeProject: patrón de repositorio, hecho correctamente


Para empezar, si está utilizando ORM completo como Entity Framework o NHibernate, debe evitar implementar una capa adicional de Repository y Unit of Work. Esto es porque; el ORM expone tanto el repositorio genérico como la unidad de trabajo.
En el caso de EF, su DbContext es Unit of Work y DbSet es Generic Repository. En el caso de NHibernate, es la propia ISession .
La construcción de un nuevo contenedor de Repositorio genérico sobre el mismo existente es un trabajo repetido. ¿Por qué reinventar la rueda?

Pero , algunos argumentan que usar ORM directamente en el código de llamada tiene los siguientes problemas:

  1. Hace que el código sea un poco más complicado.
  2. El código de acceso a los datos se fusiona en la lógica de negocios.
  3. Como muchos objetos ORM se usan en línea en el código de llamada, es muy difícil probar el código por unidad.
  4. Como ORM solo expone el repositorio genérico, causa muchos problemas que se mencionan a continuación.

Aparte de todo lo anterior, otro tema generalmente discutido es "¿Qué pasa si decidimos cambiar ORM en el futuro". Este no debe ser un punto clave al tomar una decisión porque:

  • Rara vez cambia ORM, en su mayoría NUNCA - YAGNI.
  • Si cambia ORM, tiene que hacer grandes cambios de todos modos. Puede minimizar los esfuerzos encapsulando el código completo de acceso a datos (NO solo ORM) dentro de algo . Discutiremos eso a continuación.

Teniendo en cuenta los cuatro problemas mencionados anteriormente, puede ser necesario crear Repositorios aunque esté usando ORM completo. Sin embargo , esta es una decisión por caso .

Incluso en ese caso, se debe evitar el repositorio genérico. Se considera un anti-patrón.

¿Por qué el repositorio genérico es anti-patrón?

  1. Un repositorio es una parte del dominio que se está modelando, y ese dominio no es genérico.
    • No todas las entidades pueden ser eliminadas.
    • No todas las entidades pueden ser agregadas
    • No todas las entidades tienen un repositorio.
    • Las consultas varían enormemente; La API del repositorio se vuelve tan única como la propia entidad.
    • Para GetById() , los tipos de identificadores pueden ser diferentes.
    • No es posible actualizar campos específicos (DML).
  2. El mecanismo de consulta genérico es responsabilidad de un ORM.
    • La mayoría de los ORM exponen una implementación que se parece mucho al repositorio genérico.
    • Los repositorios deben implementar las consultas ESPECÍFICAS para las entidades utilizando el mecanismo de consulta genérico expuesto por ORM.
  3. Trabajar con claves compuestas no es posible.
  4. Se escapa la lógica DAL en los servicios de todos modos.
    • Los criterios de predicado si acepta como parámetro deben proporcionarse desde la capa de servicio. Si esta es una clase específica de ORM, filtra ORM en los Servicios.

Le sugiero que lea estos artículos ( 1 , 2 , 3 , 4 , 5 ) que explican por qué el repositorio genérico es un anti-patrón. Esta other respuesta discute sobre el patrón de repositorio en general.

Por lo tanto, voy a sugerir:

  • NO use el repositorio en absoluto, use directamente ORM en su código de llamada.
  • Si tiene que usar el repositorio, entonces no intente implementar todo con el Repositorio genérico.
    En su lugar, opcionalmente cree un Repositorio Genérico muy simple y pequeño como clase base abstracta. O puede usar el repositorio genérico expuesto por su ORM como repositorio base si ORM lo permite.
    Implemente repositorios concretos según su necesidad y derive todos ellos del repositorio genérico. Exponer repositorios concretos al código de llamada.
    De esta manera usted obtiene todo el bien del repositorio genérico aún evitando sus inconvenientes.
    Aunque es muy raro, esto también ayuda a cambiar el ORM en el futuro, ya que el código ORM se abstrae claramente en DAL / Repositories. Comprenda que el cambio de ORM no es un objetivo principal de la Capa de acceso a datos o el Repositorio.

En cualquier caso, no exponga el repositorio genérico al código de llamada.

Además, no devuelva IQueryable desde repositorios concretos. Esto viola el propósito básico de la existencia de repositorios: abstraer el acceso a los datos. Al exponer IQueryable fuera del repositorio, muchas decisiones de acceso a datos se filtran al código de llamada y el Repositorio pierde el control sobre él.

¿Necesito crear un repositorio para cada entidad o implementar un repositorio genérico para el contexto?

Como se sugirió anteriormente, la creación de un repositorio para cada entidad es un mejor enfoque. Tenga en cuenta que, idealmente, el repositorio debe devolver el modelo de dominio en lugar de la entidad. Pero este es un tema diferente para la discusión.

¿Un repositorio genérico funciona para EF Database First?

Como se sugirió anteriormente, EF en sí expone el repositorio genérico. Construir una capa más es inútil. Tu imagen está diciendo lo mismo.