1. ¿Qué es?
Es un ORM (Object-Relational Mapper).
-
Función: Actúa como un traductor entre tu código C# (Clases y Objetos) y la Base de Datos (Tablas y Filas).
-
Diferencia con PHP: En lugar de escribir SQL a mano (
SELECT * FROM...), manipulas objetos en C# y EF Core escribe el SQL por ti. -
Filosofía: Usamos el enfoque “Code First” (primero escribes el código C# y la base de datos se crea a partir de él).
2. Los 3 Pilares Fundamentales
Para que funcione, necesitas estas tres piezas clave:
A. La Entidad (Entity)
Es una clase normal de C# que representa una Tabla de la base de datos.
-
Ubicación:
src/UserApi.Web/Models/User.cs -
Regla: Debe tener una propiedad
Id(clave primaria).
C#
public class User
{
public int Id { get; set; } // Primary Key automática
public string FullName { get; set; } // Columna varchar
}
B. El Contexto (DbContext)
Es la clase que representa la Sesión con la Base de Datos. Es el “puente” o gestor.
-
Ubicación:
src/UserApi.Web/Data/AppDbContext.cs -
Regla: Hereda de
DbContext.
C#
public class AppDbContext(DbContextOptions<AppDbContext> options) : DbContext(options)
{
// DbSet representa la tabla entera.
// Si no está aquí, EF Core no sabe que la tabla existe.
public DbSet<User> Users { get; set; }
}
C. La Configuración (Dependency Injection)
Debes “registrar” el Contexto en el contenedor de servicios para poder inyectarlo después.
- Ubicación:
src/UserApi.Web/Program.cs
C#
// Ejemplo usando base de datos en memoria (para desarrollo/tests)
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseInMemoryDatabase("NombreDeTuDB"));
3. Operaciones Principales (CRUD)
Lo más importante es que en .NET nunca bloqueamos el servidor. Todas las operaciones de base de datos deben ser Asíncronas (async/await).
| Acción | Código EF Core | SQL Equivalente (Aprox) |
|---|---|---|
| Leer Todo | await context.Users.ToListAsync(); | SELECT * FROM Users |
| Leer Uno | await context.Users.FindAsync(id); | SELECT * FROM Users WHERE Id = x |
| Filtrar | await context.Users.Where(u => u.Active).ToListAsync(); | SELECT * ... WHERE Active = 1 |
| Insertar | context.Users.Add(user);await context.SaveChangesAsync(); | INSERT INTO ... |
| Borrar | context.Users.Remove(user);await context.SaveChangesAsync(); | DELETE FROM ... |
⚠️ OJO:
AddyRemovesolo modifican la memoria. Nada se guarda en la base de datos real hasta que llamas aSaveChangesAsync().
4. Patrón de Uso Correcto (Arquitectura)
Nunca uses el DbContext directamente en el Controlador (Controller).
-
❌ Mal:
Controller→DbContext -
✅ Bien:
Controller→Service→DbContext
Esto permite desacoplar la lógica y facilita el Testing (como vimos con los Mocks).
5. Conceptos Avanzados que tocamos
-
In-Memory Database: Usamos
Microsoft.EntityFrameworkCore.InMemory. Es volátil (se borra al reiniciar la API). Ideal para aprender y testing rápido sin instalar SQL Server. -
Proyección (Select): No devuelvas la Entidad (
User) al mundo exterior. Úsala para leer de la DB y conviértela a un DTO (UserDto) antes de responder.C#
// Ejemplo de proyección manual var dtos = entities.Select(u => new UserDto(u.FullName, ...));
📝 Tu “Snippet” de código para Servicios
Cuando crees un servicio nuevo, esta es la plantilla mental que debes tener:
C#
public class MiServicio(AppDbContext context) : IMiServicio
{
public async Task CrearAlgo(MiDto dto)
{
// 1. Convertir DTO -> Entidad
var entidad = new MiEntidad { ... };
// 2. Añadir al contexto
context.MiTabla.Add(entidad);
// 3. ¡Guardar cambios! (Vital)
await context.SaveChangesAsync();
}
}