Módulo 18: Entity Framework Core
Objetivos del módulo
- Comprender qué es un ORM y qué problema resuelve
- Configurar Entity Framework Core con SQLite
- Crear modelos, DbContext y migraciones
- Realizar operaciones CRUD completas
- Aplicar relaciones entre entidades
1. ¿Qué es Entity Framework Core?
📘 Concepto: Entity Framework Core (EF Core) es un ORM (Object-Relational Mapper) para .NET. Permite trabajar con bases de datos usando clases de C# en lugar de escribir SQL directamente. Tus clases se convierten en tablas, y los objetos en filas.
| Sin ORM (SQL directo) | Con EF Core (C#) |
|---|---|
INSERT INTO Alumnos VALUES (...) | db.Alumnos.Add(alumno) |
SELECT * FROM Alumnos WHERE Nota > 5 | db.Alumnos.Where(a => a.Nota > 5) |
UPDATE Alumnos SET Nota = 10 WHERE Id = 1 | alumno.Nota = 10; db.SaveChanges() |
DELETE FROM Alumnos WHERE Id = 1 | db.Alumnos.Remove(alumno) |
2. Crear el proyecto
# Crear proyecto de consola
dotnet new console -n TiendaEF
cd TiendaEF
# Añadir paquetes de EF Core + SQLite
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
dotnet add package Microsoft.EntityFrameworkCore.Design
# Instalar herramienta de migraciones (una sola vez)
dotnet tool install --global dotnet-ef
Instalar paquetes NuGet desde Visual Studio Community
Si usas Visual Studio Community, también puedes instalar los paquetes desde la interfaz gráfica: Herramientas → Administrador de paquetes NuGet → Administrar paquetes NuGet para la solución…
Administrador de paquetes NuGet: busca e instala los paquetes de Entity Framework Core
3. Crear los modelos (entidades)
Los modelos son clases normales que representan las tablas de la base de datos:
// Models/Producto.cs
class Producto
{
public int Id { get; set; } // Clave primaria (convención: "Id")
public string Nombre { get; set; } = "";
public string Descripcion { get; set; } = "";
public decimal Precio { get; set; }
public int Stock { get; set; }
public DateTime FechaCreacion { get; set; } = DateTime.Now;
// Relación: un producto pertenece a una categoría
public int CategoriaId { get; set; } // Foreign key
public Categoria Categoria { get; set; } = null!; // Navegación
}
// Models/Categoria.cs
class Categoria
{
public int Id { get; set; }
public string Nombre { get; set; } = "";
public string Descripcion { get; set; } = "";
// Relación inversa: una categoría tiene muchos productos
public List<Producto> Productos { get; set; } = new();
}
4. Crear el DbContext
📘 Concepto: El
DbContextes la clase principal de EF Core. Representa la sesión con la base de datos y contieneDbSet<T>para cada tabla.
// Data/TiendaContext.cs
using Microsoft.EntityFrameworkCore;
class TiendaContext : DbContext
{
// Cada DbSet<T> representa una tabla
public DbSet<Producto> Productos { get; set; }
public DbSet<Categoria> Categorias { get; set; }
// Configurar la conexión a SQLite
protected override void OnConfiguring(DbContextOptionsBuilder options)
{
options.UseSqlite("Data Source=tienda.db");
}
// Configuraciones adicionales (opcional)
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Datos iniciales (seed)
modelBuilder.Entity<Categoria>().HasData(
new Categoria { Id = 1, Nombre = "Electrónica", Descripcion = "Dispositivos electrónicos" },
new Categoria { Id = 2, Nombre = "Ropa", Descripcion = "Prendas de vestir" },
new Categoria { Id = 3, Nombre = "Hogar", Descripcion = "Artículos para el hogar" }
);
}
}
5. Crear y aplicar migraciones
Las migraciones son la forma de crear y actualizar la estructura de la base de datos:
# Crear la primera migración
dotnet ef migrations add Inicial
# Aplicar la migración (crea la base de datos y las tablas)
dotnet ef database update
# Si cambias los modelos, crea una nueva migración
dotnet ef migrations add AgregarCampoDescripcion
dotnet ef database update
6. Operaciones CRUD
Create (Crear)
using var db = new TiendaContext();
// Crear un producto
var producto = new Producto
{
Nombre = "Teclado mecánico",
Descripcion = "Teclado Cherry MX Blue",
Precio = 89.99m,
Stock = 50,
CategoriaId = 1 // Electrónica
};
db.Productos.Add(producto);
await db.SaveChangesAsync(); // Guarda en la base de datos
Console.WriteLine($"Producto creado con Id: {producto.Id}");
Read (Leer)
using var db = new TiendaContext();
// Obtener todos
List<Producto> todos = await db.Productos.ToListAsync();
// Obtener por Id
Producto? producto = await db.Productos.FindAsync(1);
// Filtrar con LINQ
var baratos = await db.Productos
.Where(p => p.Precio < 50)
.OrderBy(p => p.Nombre)
.ToListAsync();
// Incluir datos relacionados (JOIN automático)
var conCategoria = await db.Productos
.Include(p => p.Categoria) // Carga la categoría asociada
.Where(p => p.Stock > 0)
.ToListAsync();
foreach (var p in conCategoria)
{
Console.WriteLine($"{p.Nombre} ({p.Categoria.Nombre}): {p.Precio:C2}");
}
// Primer elemento / existe
var primero = await db.Productos.FirstOrDefaultAsync(p => p.Nombre.Contains("Teclado"));
bool existe = await db.Productos.AnyAsync(p => p.Precio > 100);
int total = await db.Productos.CountAsync();
Update (Actualizar)
using var db = new TiendaContext();
var producto = await db.Productos.FindAsync(1);
if (producto is not null)
{
producto.Precio = 79.99m;
producto.Stock += 20;
await db.SaveChangesAsync(); // EF detecta los cambios automáticamente
Console.WriteLine("Producto actualizado");
}
Delete (Eliminar)
using var db = new TiendaContext();
var producto = await db.Productos.FindAsync(1);
if (producto is not null)
{
db.Productos.Remove(producto);
await db.SaveChangesAsync();
Console.WriteLine("Producto eliminado");
}
7. Relaciones
Uno a muchos (1:N)
Ya lo vimos: una Categoría tiene muchos Productos.
// Crear categoría con productos
var categoria = new Categoria
{
Nombre = "Periféricos",
Productos = new List<Producto>
{
new() { Nombre = "Ratón", Precio = 29.99m, Stock = 100 },
new() { Nombre = "Auriculares", Precio = 59.99m, Stock = 75 }
}
};
db.Categorias.Add(categoria);
await db.SaveChangesAsync(); // Crea la categoría Y los 2 productos
// Consultar con Include
var cats = await db.Categorias
.Include(c => c.Productos)
.ToListAsync();
foreach (var c in cats)
{
Console.WriteLine($"\n{c.Nombre}:");
foreach (var p in c.Productos)
Console.WriteLine($" - {p.Nombre}: {p.Precio:C2}");
}
Muchos a muchos (N:M)
class Pedido
{
public int Id { get; set; }
public DateTime Fecha { get; set; } = DateTime.Now;
public string Cliente { get; set; } = "";
// Relación N:M con Producto a través de DetallePedido
public List<DetallePedido> Detalles { get; set; } = new();
}
class DetallePedido
{
public int Id { get; set; }
public int Cantidad { get; set; }
public decimal PrecioUnitario { get; set; }
public int PedidoId { get; set; }
public Pedido Pedido { get; set; } = null!;
public int ProductoId { get; set; }
public Producto Producto { get; set; } = null!;
public decimal Total => Cantidad * PrecioUnitario;
}
8. Consultas avanzadas con LINQ + EF Core
using var db = new TiendaContext();
// Agrupar productos por categoría con estadísticas
var resumen = await db.Productos
.Include(p => p.Categoria)
.GroupBy(p => p.Categoria.Nombre)
.Select(g => new
{
Categoria = g.Key,
TotalProductos = g.Count(),
PrecioMedio = g.Average(p => p.Precio),
StockTotal = g.Sum(p => p.Stock),
ProductoMasCaro = g.Max(p => p.Precio)
})
.OrderByDescending(r => r.TotalProductos)
.ToListAsync();
foreach (var r in resumen)
{
Console.WriteLine($"{r.Categoria}: {r.TotalProductos} productos, " +
$"precio medio {r.PrecioMedio:C2}, stock total {r.StockTotal}");
}
// Búsqueda con paginación
int pagina = 1;
int tamano = 10;
var paginaProductos = await db.Productos
.OrderBy(p => p.Nombre)
.Skip((pagina - 1) * tamano)
.Take(tamano)
.ToListAsync();
9. Ejercicios
Ejercicio 1: Biblioteca con EF Core
Crea una base de datos para una biblioteca con: Libro (Id, Titulo, ISBN, Año), Autor (Id, Nombre, Pais), relación N:M entre libros y autores. Implementa CRUD completo + búsquedas (por título, por autor, por año).
Ejercicio 2: Gestión de notas
Crea un sistema con: Alumno (Id, Nombre, Email), Asignatura (Id, Nombre, Creditos), Nota (AlumnoId, AsignaturaId, Calificacion, Convocatoria). Permite: matricular, poner notas, ver expediente, calcular media ponderada.
Ejercicio 3: Tienda online completa
Expande el ejemplo de la tienda con: Cliente, Pedido, DetallePedido. Implementa: crear pedido, listar pedidos de un cliente, informes de ventas (por producto, por mes, por cliente), stock automático.
Resumen
| Operación | Código EF Core |
|---|---|
| Crear | db.Items.Add(item); await db.SaveChangesAsync(); |
| Leer todos | await db.Items.ToListAsync(); |
| Leer por Id | await db.Items.FindAsync(id); |
| Filtrar | await db.Items.Where(i => ...).ToListAsync(); |
| Incluir relación | .Include(i => i.Relacion) |
| Actualizar | Modificar propiedades + SaveChangesAsync() |
| Eliminar | db.Items.Remove(item); SaveChangesAsync() |
| Crear migración | dotnet ef migrations add Nombre |
| Aplicar migración | dotnet ef database update |