Recientemente en mi trabajo iniciamos el desarrollo de una aplicación donde varios desarrolladores íbamos a estar trabajando al mismo tiempo, cada quien en módulos diferentes. Y como suele pasar esto implica cambios constantes en la base de datos.
Si has trabajado con Entity Framework en equipo, probablemente ya te has topado con el problema de que el DbContext empieza a desincronizarse, migraciones que chocan, modelos que ya no representan la realidad de la base de datos y terminas perdiendo tiempo en cosas que no deberían ser problema.
Así que decidí buscar una alternativa más simple, más directa y que no presentara este problema. Ahí fue donde me encontré con Dapper.
¿Qué es Dapper?
Dapper es un micro ORM desarrollado por el equipo de Stack Overflow que diferencia de Entity Framework, aquí no hay magia pesada ni abstracciones complejas, escribes tu SQL y Dapper se encarga de mapear los resultados a tus objetos.
¿Por qué decidí usarlo?
En mi caso, lo que necesitaba era:
- Evitar problemas de sincronización entre modelo y base de datos
- Tener control total sobre las consultas
- Algo rápido de implementar
- Que funcionara bien trabajando en equipo
Aplicación de Control Gastos
Para probar Dapper decidí hacer una aplicación ultra sencilla para registrar gastos ya que necesitaba aprender a usarlo antes de implementarlo en un proyecto en mi trabajo.
Instalación
Nada complicado, como cualquier paquete de NuGet:
Install-Package Dapper
Y para SQL Server:
dotnet add package Microsoft.Data.SqlClient
Configuración básica
Yo normalmente manejo una clase base para obtener la conexión, algo así:
public class BaseRepository
{
private readonly IConfiguration _configuration;
public BaseRepository(IConfiguration configuration)
{
_configuration = configuration;
}
protected SqlConnection GetConnection()
{
return new SqlConnection(_configuration.GetConnectionString("DefaultConnection"));
}
}Esto me permite reutilizar la conexión en todos mis repositorios.
Modelo de ejemplo
public class Usuario
{
public int Id { get; set; }
public string Nombre { get; set; }
public string Email { get; set; }
}SELECT
Aquí es donde Dapper brilla por su simplicidad.
public Usuario[] GetUsuarios()
{
using var con = GetConnection();
return con.Query<Usuario>("SELECT * FROM Usuarios")
.ToArray();
}Con parámetros:
public Usuario? GetUsuarioById(int id)
{
using var con = GetConnection();
return con.QueryFirstOrDefault<Usuario>(
"SELECT * FROM Usuarios WHERE Id = @Id",
new { Id = id });
}INSERT
public void InsertUsuario(Usuario usuario)
{
using var con = GetConnection();
con.Execute(
"INSERT INTO Usuarios (Nombre, Email) VALUES (@Nombre, @Email)",
usuario);
}UPDATE
public void UpdateUsuario(Usuario usuario)
{
using var con = GetConnection();
con.Execute(
@"UPDATE Usuarios
SET Nombre = @Nombre, Email = @Email
WHERE Id = @Id",
usuario);
}DELETE
public void DeleteUsuario(int id)
{
using var con = GetConnection();
con.Execute(
"DELETE FROM Usuarios WHERE Id = @Id",
new { Id = id });
}Transacciones
Aquí es donde ya se pone interesante, porque puedes hacer exactamente lo que necesitas sin pelearte con el ORM.
En uno de mis repositorios, por ejemplo, manejo algo así:
- Inserto una cuenta
- Si tiene saldo inicial, inserto también un movimiento
- Todo dentro de una transacción
Ejemplo simplificado:
using var con = GetConnection();
con.Open();
using var trx = con.BeginTransaction();
try
{
var cuentaId = con.ExecuteScalar<long>(
@"INSERT INTO Cuentas (Nombre)
VALUES (@Nombre);
SELECT SCOPE_IDENTITY();",
new { Nombre = "Cuenta demo" },
trx);
con.Execute(
@"INSERT INTO Movimientos (CuentaId, Importe)
VALUES (@CuentaId, @Importe)",
new { CuentaId = cuentaId, Importe = 100 },
trx);
trx.Commit();
}
catch
{
trx.Rollback();
throw;
}Este tipo de control es justo lo que estaba buscando.
Ventajas
Después de usarlo en algunos proyectos en mi día a día, estas fueron las principales ventajas:
- Menos fricción trabajando en equipo
- No dependes de migraciones
- Código más predecible
- Mejor rendimiento
- Debug mucho más sencillo
¿Reemplaza a Entity Framework?
No necesariamente, Entity Framework sigue siendo muy útil en muchos escenarios, sobre todo cuando:
- No quieres escribir SQL
- Tu modelo es muy estable
- Necesitas rapidez para prototipar
Pero en escenarios como el mío (muchos cambios y varios desarrolladores), creo Dapper se siente mucho más cómodo.
Conclusión
En mi caso, Dapper no solo resolvió un problema que me representaría usar Entity Framework sino que me ayudó a mejorar el flujo de trabajo al no tener que sincronizar mi base de datos con el modelo. Aunque no descarto la idea de usar Entity Framework pero será en una etapa donde los cambios en la base de datos no sean tan recurrentes como ahora.
Deja un comentario