En este artículo, voy a explicar como crear un repositorio genérico en EF7. El patrón Repositorio es una construcción muy común para evitar la duplicación de la lógica de acceso a datos a través de nuestra aplicación. El propósito del repositorio es ocultar los detalles del acceso a los datos. Podemos consultar fácilmente el repositorio de objetos de datos, sin tener que saber cómo proporcionar datos como la cadena de conexión. El patrón Repositorio añade una capa entre los datos y las capas de dominio de una aplicación. También hace que las partes de acceso a los datos de una aplicación sean mejor testeables.
Esta es la versión inicial de la interfaz del repositorio:
public interface IEmployeeRepository { Task < Employee > Get ( Guid ? id ); Task Save ( Employee employee ); Task Delete ( Employee employee ); Task Update ( Employee employee ); Task < IEnumerable < Employee >> FindAll (); }
Esto es específico para la clase Employee, repositorio que contiene las operaciones CRUD. Y aquí está la implementación de la clase del repositorio Employee con DbContext.
public class EmployeeRepository : IEmployeeRepository { private EmployeeContext _employeeContext ; public EmployeeRepository () { _employeeContext = new EmployeeContext (); } public async Task < Employee > Get ( Guid ? id ) { return await _employeeContext . Employees . FirstOrDefaultAsync ( x => x . Id == id ); } public async Task Save ( Employee employee ) { _employeeContext . Employees . Add ( employee ); await _employeeContext . SaveChangesAsync (); } public async Task Delete ( Employee employee ) { _employeeContext . Employees . Remove ( employee ); await _employeeContext . SaveChangesAsync (); } public async Task Update ( Employee employee ) { _employeeContext . Employees . Update ( employee ); await _employeeContext . SaveChangesAsync (); } public async Task < IEnumerable < Employee >> FindAll () { return await _employeeContext . Employees . ToListAsync (); } }
Y este es el Employee context object:
public class EmployeeContext : DbContext { private static bool _created = false ; public EmployeeContext () { if (! _created ) { Database . EnsureCreated (); _created = true ; } } public DbSet < Employee > Employees { get ; set ; } protected override void OnConfiguring ( EntityOptionsBuilder optionsBuilder ) { optionsBuilder . UseInMemoryStore (); } }
Aquí tenemos dos problemas con la actual implementación de EmployeeRepository. El primero de ellos está al utilizar una model class, el Employee, si tiene varias model class, tendremos que duplicar mucho código para desarrollarlo correctamente. El segundo de ellos es que no es comprobable. El primer problema se puede solucionar haciéndolo genérico. Y el segundo problema se puede resolver mediante la inyección del object context. Aquí está la interfaz del repositorio genérico:
public interface IGenericRepository < T > where T : class , IEntity , new () { Task < T > Get ( Guid ? id ); Task Save ( T employee ); Task Delete ( T employee ); Task Update ( T employee ); Task < IEnumerable < T >> FindAll (); }
La interfaz IEntity contiene solo una propiedad, Id.
public interface IEntity { Guid Id { get ; set ; } }
Y aquí tenéis la implementación de la clase GenericRepository:
public class GenericRepository < T > : IGenericRepository < T > where T : class , IEntity , new () { private DbContext _dbContext ; public GenericRepository ( DbContext dbContext ) { _dbContext = dbContext ; } public async Task Delete ( T employee ) { _dbContext . Set < T >(). Remove ( employee ); await _dbContext . SaveChangesAsync (); } public async Task < IEnumerable < T >> FindAll () { return await _dbContext . Set < T >(). ToListAsync (); } public async Task < T > Get ( Guid ? id ) { return await _dbContext . Set < T >(). FirstOrDefaultAsync ( x => x . Id == id ); } public async Task Save ( T employee ) { _dbContext . Set < T >(). Add ( employee ); await _dbContext . SaveChangesAsync (); } public async Task Update ( T employee ) { _dbContext . Set < T >(). Update ( employee ); await _dbContext . SaveChangesAsync (); } }
En esta implementación ocurre un problema más. En la implementación del DbContext necesitas la referencia del modelo de Employee. Puedes hacer a DbContext también generica usando Reflection:
public class GenericDbContext : DbContext { private static bool _created = false ; public GenericDbContext () { if (! _created ) { Database . EnsureCreated (); _created = true ; } } protected override void OnConfiguring ( DbContextOptionsBuilder optionsBuilder ) { optionsBuilder . UseInMemoryDatabase ( true ); } protected override void OnModelCreating ( ModelBuilder modelBuilder ) { var types = Assembly . GetExecutingAssembly (). GetTypes () . Where ( type => typeof ( IEntity ). IsAssignableFrom ( type ) && type . IsClass ); var method = typeof ( ModelBuilder ). GetMethods (). First ( m => m . Name == "Entity" && m . IsGenericMethodDefinition && m . GetParameters (). Length == 0 ); foreach ( var type in types ) { method = method . MakeGenericMethod ( type ); method . Invoke ( modelBuilder , null ); } base . OnModelCreating ( modelBuilder ); } }
En el método OnModelCreating, todos los tipos que implementan la interfaz de IEntity son añadidos a DbContext usando el método Entity(). Este método es invocado dinámicamente usando Reflection. En ASP.net 5 puedes inyectar el repositorio y el contexto usando inyecciones de dependencias:
public void ConfigureServices ( IServiceCollection services ) { services . AddMvc (); services . AddScoped < DbContext , GenericDbContext >(); services . AddScoped < IGenericRepository < Employee >, GenericRepository < Employee >>(); }
Y hasta aquí el código para crear repositorios en ASP.net 5. Espero que este tutorial os haya gustado y sepáis utilizarlo en vuestros propios proyectos. Ya sabéis que nos podéis proponer temas a través de los comentarios de más abajo y enviar vuestros propios tutoriales a través de la intranet para usuarios de programacion.net.
¡Feliz programación!
Fuente: Anuraj Parameswaran