La Magia del Patrón Repositorio en C#

Si estás familiarizado con el mundo de desarrollo de software en .NET, probablemente hayas oído hablar del patrón de repositorio (Repository pattern). A veces, este patrón se considera "pasado de moda" en la era de los microservicios y las bases de datos NoSQL. Pero estoy aquí para decirte que sigue siendo tan relevante y útil como siempre.

 

 

1 - Qué es el Patrón Repositorio?

El patrón de repositorio es una abstracción de la capa de datos. En esencia, un repositorio es como una caja negra que puede recibir y enviar datos de y a tu base de datos. Lo bueno del  patrón repositorio es que, desde la perspectiva del resto de tu código, no importa qué tipo de base de datos estés utilizando o cómo esté implementada. O en otras palabras, estamos abstrayendo la implementación.

 

Digamos que tienes una aplicación con una base de datos MySQL. Tu aplicación necesita recuperar y guardar datos en esta base de datos. En lugar de dispersar consultas SQL en todo tu código, puedes encapsular todas estas operaciones en un repositorio. Así, si en un futuro quieres cambiar de base de datos, el proceso es mucho más sencillo. 

 

 

2 - Por qué es útil el Patrón Repositorio?

Un beneficio obvio del patrón de repositorio es que hace que tu código sea mucho más limpio y más fácil de mantener. 

 

Pero también tiene otras ventajas. Por ejemplo, si en algún momento en el futuro decides cambiar a una base de datos diferente, sólo necesitarás actualizar tus repositorios, no todo tu código. (Aunque en verdad nunca nadie cambia la base de datos).

 

Además, el patrón repositorio hace que tu código sea mucho más fácil de probar. Puedes usar un 'mock' de tu repositorio en los test unitarios, que simula el comportamiento de tu base de datos. Esto te permite probar tu lógica de negocio sin tener que preocuparte de configurar una base de datos de prueba.

 

2.1 - Es Dbcontext y DbSet una implementación del patrón repositorio? 

Técnicamente es cierto que cuando utilizamos entity framework DbContext se asemeja a lo que es la unidad de trabajo y el DbSet a una implementación del repositorio.

Pese a ellos, algunos, entre los que me incluyo, preferimos tener una capa adicional de abstracción encima de la base de datos, ya que así es más fácil de probar y está desacoplado de entity framework.

 

Pero como todo es una decisión de diseño, que tiene pros y contras y dependerá de las necesidades del equipo. 

 

 

3 - Cuándo utilizar el patrón repositorio?

Como todos los patrones de diseño, el patrón repositorio no es obligatorio en todas las implementaciones. 

En algunas situaciones, puede ser una sobrecarga innecesaria, especialmente si tu aplicación es muy simple y sólo necesitas hacer operaciones básicas de CRUD. 

 

Además, a veces querrás usar características específicas de tu base de datos que no se pueden encapsular fácilmente en un repositorio.

 

 

4 - Implementar el patrón repositorio en C#

Antes de empezar con el código, recuerda que lo tienes disponible en GitHub. Para este post, vamos a continuar con el código que hemos estado  trabajando en el curso de entity framework core.

Para este ejemplo vamos a coger el código que vimos el otro dia, donde insertamos un usuario dentro de la tabla Users.

[HttpPost("InsertDataExample2")]
public async Task InsertDataExample2()
{
    User user1 = new User()
    {
        Email = $"{Guid.NewGuid()}@mail.com",
        UserName = "id1",
        Wokringexperiences = new List<Wokringexperience>()
        {
            new Wokringexperience()
            {
                Name = "experience 1 same object",
                Details = "details1",
                Environment = "environment"
            },
            new Wokringexperience()
            {
                Name = "experience 2 same object",
                Details = "details2",
                Environment = "environment"
            }
        }
    };


    await _context.Users.AddAsync(user1);
    await _context.SaveChangesAsync();
}

Lo que vamos a hacer es modificar el código para utilizar el patrón repositorio. 

Nota: si seguiste el post anterior donde trabajamos con claves foraneas sabrás que en otro endpoint hicimos el siguiente código: 

await _context.Users.AddAsync(user1);
await _context.Wokringexperiences.AddRangeAsync(workingExperiences1);
await _context.SaveChangesAsync();

Este NO lo vamos a modificar, ya que para insertar en dos tablas de forma independiente requerimos una transacción, y veremos dicha transacción cuando veamos el patrón unitOfWork en el siguiente post.

 

Volvamos al lío, si has prestado atención, lo que hacemos con el patrón repositorio es abstraer la implementación, y en C# abstraemos con interfaces. 

Para ello creamos una interfaz llamada IUserRepository y una clase la cual actuará como dicho repositorio.

public interface IUserRepository
{
    
}

public class UserRepository : IUserRepository
{
    
}

Ahora lo que debemos hacer en nuestro repositorio es la interacción con la base de datos, en nuestro caso, inyectamos el DBContext y creamos un método que se llame Insert, el cual insertará el registro en la base de datos. 

 

Y ya que estamos hacemos otro para la consulta de leer por id.

public interface IUserRepository
{
    Task<User> Insert(User user);
    Task<User?> GetById(int id);
}

public class UserRepository : IUserRepository
{
    private readonly CursoEfContext _context;

    public UserRepository(CursoEfContext context)
    {
        _context = context;
    }

    public async Task<User> Insert(User user)
    {
        EntityEntry<User> insertedUser = await _context.Users.AddAsync(user);
        await _context.SaveChangesAsync();
        return insertedUser.Entity;
    }

    public async Task<User?> GetById(int id)
        => await _context.Users
            .Include(a => a.Wokringexperiences)
            .FirstOrDefaultAsync(x => x.Id == id);
}

Como puedes observar, estamos guardando la inserción dentro del propio repositorio. Esto cambiará cuando tengamos transacciones.

 

Otra cosa muy común en empresas es tener una clase/interfaz llamada IGenericRepository<T> donde T es el generic de un tipo en concreto y ya nos trae implementadas las acciones básicas como son insertar, borrar, actualizar y leer.

 

Ahora solo nos queda incluir nuestro nuevo repositorio en las dependencias y hacer el cambio a la lógica para que se inserte con el repositorio en vez de directamente con el DBContext.

[HttpPost("InsertDataExample2")]
public async Task InsertDataExample2()
{
    User user1 = new User()
    {
        Email = $"{Guid.NewGuid()}@mail.com",
        UserName = "id1",
        Wokringexperiences = new List<Wokringexperience>()
        {
            new Wokringexperience()
            {
                Name = "experience 1 same object",
                Details = "details1",
                Environment = "environment"
            },
            new Wokringexperience()
            {
                Name = "experience 2 same object",
                Details = "details2",
                Environment = "environment"
            }
        }
    };

    await _userRepository.Insert(user1);
}

[HttpGet("{userId}")]
public async Task<User?> GetExample(int userId)
    => await _userRepository.GetById(userId);

Y si ejecutamos el código, vemos cómo se inserta correctamente. Si ejecutamos el endpoint de Get por id, podemos ver el resultado.

GET https://localhost:44383/Relations/3

{
  "id": 3,
  "userName": "id1",
  "email": "[email protected]",
  "wokringexperiences": [
    {
      "id": 1,
      "userId": 3,
      "name": "experience 1 same object",
      "details": "details1",
      "environment": "environment",
      "startDate": null,
      "endDate": null
    },
    {
      "id": 2,
      "userId": 3,
      "name": "experience 2 same object",
      "details": "details2",
      "environment": "environment",
      "startDate": null,
      "endDate": null
    }
  ]
}

Nota: es el id:3 porque los otros dos se crean al hacer el spin up del sistema. 

 


Uso del bloqueador de anuncios adblock

Hola!

Primero de todo bienvenido a la web de NetMentor donde podrás aprender programación en C# y .NET desde un nivel de principiante hasta más avanzado.


Yo entiendo que utilices un bloqueador de anuncios como AdBlock, Ublock o el propio navegador Brave. Pero te tengo que pedir por favor que desactives el bloqueador para esta web.


Intento personalmente no poner mucha publicidad, la justa para pagar el servidor y por supuesto que no sea intrusiva; Si pese a ello piensas que es intrusiva siempre me puedes escribir por privado o por Twitter a @NetMentorTW.


Si ya lo has desactivado, por favor recarga la página.


Un saludo y muchas gracias por tu colaboración

© copyright 2024 NetMentor | Todos los derechos reservados | RSS Feed

Buy me a coffee Invitame a un café