Actualmente hemos implementado un patrón de repositorio en el trabajo. Todos nuestros repositorios se sientan detrás de sus propias interfaces y se asignan a través de Ninject. Nuestro proyecto es bastante grande y hay un par de peculiaridades con este patrón que estoy tratando de resolver.Intentando simplificar nuestro patrón de repositorio
Primero, hay algunos controladores donde necesitamos más de 10 a 15 repositorios, todos en el mismo controlador. El constructor se pone bastante feo cuando pregunta por tantos repositorios. La segunda peculiaridad se revela después de llamar a métodos en múltiples repositorios. Después de trabajar con varios repositorios, necesitamos llamar al método SaveChanges, pero ¿a qué repositorio debemos llamarlo? Cada repositorio tiene uno. Todos los repositorios tienen la misma instancia del contexto de datos de Entity Framework inyectado, por lo que seleccionar cualquier repositorio aleatorio para llamar a guardar funcionará. Parece tan complicado.
Busqué el patrón de "Unidad de trabajo" y encontré una solución que creo que resuelve ambos problemas, pero no estoy 100% seguro en esta solución, por lo que cualquier comentario es apreciado. Creé una clase llamada DataBucket
(sobre todo porque no me gusta llamarla UnitOfWork
. Suena gracioso).
// Slimmed down for readability
public class DataBucket
{
private DataContext _dataContext;
public IReportsRepository ReportRepository { get; set; }
public IEmployeeRepository EmployeeRepository { get; set; }
public IDashboardRepository DashboardRepository { get; set; }
public DataBucket(DataContext dataContext,
IReportsRepository reportsRepository,
IEmployeeRepository employeeRepository,
IDashboardRepository dashboardRepository)
{
_dataContext = dataContext;
this.ReportRepository = reportsRepository;
this.EmployeeRepository = employeeRepository;
this.DashboardRepository = dashboardRepository;
}
public void SaveChanges()
{
_dataContext.SaveChanges();
}
}
Esto parece resolver ambos problemas. Ahora solo hay un método SaveChanges
en el contenedor de datos y solo se inyecta un objeto, el contenedor de datos. A continuación, accede a todos los repositorios como propiedades. El cubo de datos sería un poco desordenado ya que estaría aceptando TODO (fácilmente 50 o más) de nuestros repositorios en su constructor.
El proceso de agregar un nuevo repositorio ahora incluiría: crear la interfaz, crear el repositorio, mapear la interfaz y el repositorio en Ninject, y agregar una propiedad al contenedor de datos y rellenarlo.
Pensé en una alternativa a esto que eliminaría un paso de arriba.
public class DataBucket
{
private DataContext _dataContext;
public IReportsRepository ReportRepository { get; set; }
public IEmployeeRepository EmployeeRepository { get; set; }
public IDashboardRepository DashboardRepository { get; set; }
public DataBucket(DataContext dataContext)
{
_dataContext = dataContext;
this.ReportRepository = new ReportsRepository(dataContext);
this.EmployeeRepository = new EmployeeRepository(dataContext);
this.DashboardRepository = new DashboardRepository(dataContext);
}
public void SaveChanges()
{
_dataContext.SaveChanges();
}
}
Ésta elimina prácticamente todas las asignaciones del repositorio en Ninject porque todos están instanciados en el cubo de datos. Así que ahora los pasos para agregar un nuevo repositorio incluyen: Crear interfaz, crear repositorio, agregar propiedad al depósito de datos e instanciar.
¿Pueden ver algunos defectos con este modelo? En la superficie, parece mucho más conveniente consumir nuestros repositorios de esta manera. ¿Es este un problema que se ha abordado anteriormente? De ser así, ¿cuál es el enfoque más común y/o más eficiente para este problema?
Gracias :)
¿No necesita una interfaz IDataBucket ya que esta clase usa las clases de repositorio, por lo que esta clase también se inyecta? Aparte de eso, parece una buena solución para mí. Creo que sería mejor hacer que las propiedades del repositorio tengan un setter privado. – Maarten
Puede inyectar clases concretas con Ninject. Si no proporciona ninguna asignación, Ninject buscará una clase con el nombre solicitado e instanciará si es posible. – Chev
Por motivos de unidad aunque @Maarten probablemente tengas razón. Tendré que interconectarlo para poder simularlo para probarlo. – Chev