Repositorio genérico con ASP.net 5

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

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP