Introducción a Entity Framework Core
En el desarrollo de aplicaciones modernas, trabajar con bases de datos es una tarea fundamental. Hasta ahora hemos visto cómo manejar archivos de texto y JSON, pero las aplicaciones empresariales requieren sistemas de almacenamiento más robustos y escalables. Entity Framework Core (EF Core) es un mapeador objeto-relacional (ORM) moderno y ligero que nos permite trabajar con bases de datos utilizando objetos de C#, eliminando la necesidad de escribir gran parte del código de acceso a datos.
EF Core actúa como un puente entre el mundo orientado a objetos de C# y el mundo relacional de las bases de datos. Esto significa que podemos trabajar con tablas, filas y columnas utilizando clases, objetos y propiedades, lo que hace que el desarrollo sea más intuitivo y productivo. Además, EF Core se integra perfectamente con LINQ, permitiendo escribir consultas de base de datos utilizando sintaxis familiar de C#.
En este artículo aprenderemos los conceptos fundamentales de Entity Framework Core, cómo configurarlo en un proyecto, crear nuestro primer modelo de datos y realizar operaciones básicas de conexión con la base de datos.
¿Qué es Entity Framework Core?
Entity Framework Core es un framework de acceso a datos de código abierto desarrollado por Microsoft. Es la evolución moderna del Entity Framework tradicional, diseñado para ser multiplataforma, ligero y de alto rendimiento.
Características principales de EF Core
Característica | Descripción |
---|---|
Multiplataforma | Funciona en Windows, Linux, macOS |
Proveedores múltiples | Soporta SQL Server, SQLite, PostgreSQL, MySQL y más |
Code First | Permite definir el modelo usando clases de C# |
Database First | Puede generar modelos desde bases de datos existentes |
Migraciones | Sistema automático para evolucionar el esquema de la base de datos |
LINQ integrado | Consultas fuertemente tipadas usando sintaxis familiar |
Seguimiento de cambios | Detección automática de modificaciones en entidades |
Carga perezosa | Carga datos relacionados solo cuando se necesitan |
Conceptos fundamentales
Entidad (Entity): Una clase de C# que representa una tabla en la base de datos. Cada instancia de la entidad corresponde a una fila en la tabla.
Contexto (DbContext): La clase principal que coordina la funcionalidad de EF Core. Representa una sesión con la base de datos y permite consultar y guardar datos.
Modelo (Model): El conjunto de clases de entidad y la configuración que define la estructura de la base de datos.
Proveedor de base de datos: Un plugin que permite a EF Core comunicarse con un tipo específico de base de datos.
Instalación y configuración inicial
Para comenzar a trabajar con Entity Framework Core, necesitamos instalar los paquetes NuGet correspondientes. En este tutorial utilizaremos SQLite por su simplicidad, ya que no requiere instalación de servidor.
Instalación de paquetes NuGet
Desde la consola del administrador de paquetes de Visual Studio o la terminal, ejecutamos:
dotnet add package Microsoft.EntityFrameworkCore
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
dotnet add package Microsoft.EntityFrameworkCore.Tools
Estos paquetes nos proporcionan:
- Microsoft.EntityFrameworkCore: El núcleo de EF Core
- Microsoft.EntityFrameworkCore.Sqlite: El proveedor para bases de datos SQLite
- Microsoft.EntityFrameworkCore.Tools: Herramientas de línea de comandos para migraciones
Configuración básica del proyecto
Vamos a crear un ejemplo práctico con un sistema simple de gestión de libros. Primero definimos nuestras entidades:
using System.ComponentModel.DataAnnotations;
namespace BibliotecaApp.Models
{
// Entidad que representa un libro en la base de datos
public class Libro
{
public int Id { get; set; }
[Required]
[MaxLength(200)]
public string Titulo { get; set; }
[Required]
[MaxLength(100)]
public string Autor { get; set; }
[Range(1, int.MaxValue)]
public int Paginas { get; set; }
public DateTime FechaPublicacion { get; set; }
public decimal Precio { get; set; }
public bool Disponible { get; set; } = true;
}
}
Creación del contexto de base de datos
El DbContext
es el corazón de cualquier aplicación EF Core. Esta clase coordina todas las operaciones con la base de datos:
using Microsoft.EntityFrameworkCore;
using BibliotecaApp.Models;
namespace BibliotecaApp.Data
{
// Contexto que representa nuestra sesión con la base de datos
public class BibliotecaContext : DbContext
{
// Constructor que recibe las opciones de configuración
public BibliotecaContext(DbContextOptions<BibliotecaContext> options)
: base(options)
{
}
// DbSet representa una tabla en la base de datos
public DbSet<Libro> Libros { get; set; }
// Método para configurar el modelo de datos
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// Configuración adicional del modelo
modelBuilder.Entity<Libro>(entity =>
{
entity.HasKey(e => e.Id);
entity.Property(e => e.Precio)
.HasPrecision(10, 2); // 10 dígitos, 2 decimales
});
}
}
}
Configuración de la cadena de conexión
En aplicaciones de consola, configuramos la conexión directamente en el programa principal:
using Microsoft.EntityFrameworkCore;
using BibliotecaApp.Data;
using BibliotecaApp.Models;
class Program
{
static async Task Main(string[] args)
{
// Configuración del contexto con SQLite
var optionsBuilder = new DbContextOptionsBuilder<BibliotecaContext>();
optionsBuilder.UseSqlite("Data Source=biblioteca.db");
// Creación del contexto
using var context = new BibliotecaContext(optionsBuilder.Options);
// Asegurar que la base de datos existe
await context.Database.EnsureCreatedAsync();
Console.WriteLine("Base de datos creada y configurada correctamente.");
// Ejemplo de uso básico
await EjemploBasico(context);
}
static async Task EjemploBasico(BibliotecaContext context)
{
// Agregar un libro de ejemplo
var libro = new Libro
{
Titulo = "El Quijote",
Autor = "Miguel de Cervantes",
Paginas = 863,
FechaPublicacion = new DateTime(1605, 1, 16),
Precio = 25.99m,
Disponible = true
};
context.Libros.Add(libro);
await context.SaveChangesAsync();
Console.WriteLine($"Libro '{libro.Titulo}' agregado con ID: {libro.Id}");
// Consultar todos los libros
var libros = await context.Libros.ToListAsync();
Console.WriteLine($"\nTotal de libros en la biblioteca: {libros.Count}");
foreach (var l in libros)
{
Console.WriteLine($"- {l.Titulo} por {l.Autor} ({l.FechaPublicacion.Year})");
}
}
}
Convenciones y configuraciones
Entity Framework Core utiliza un conjunto de convenciones para mapear las clases de C# a las tablas de la base de datos. Sin embargo, también podemos personalizar este comportamiento.
Convenciones predeterminadas
Convención | Comportamiento |
---|---|
Nombre de tabla | Nombre de la propiedad DbSet (ej: Libros ) |
Clave primaria | Propiedad llamada Id o [ClaseName]Id |
Tipo de columna | Inferido del tipo de propiedad de C# |
Longitud de cadena | Máxima permitida por la base de datos |
Valores nulos | Permitidos para tipos nullables |
Configuración mediante atributos
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
[Table("LibrosCatalogo")] // Nombre personalizado de tabla
public class Libro
{
[Key] // Clave primaria explícita
public int LibroId { get; set; }
[Required] // Campo obligatorio
[MaxLength(200)] // Longitud máxima
public string Titulo { get; set; }
[Column(TypeName = "varchar(100)")] // Tipo específico de columna
public string Autor { get; set; }
[Range(1, 10000)] // Validación de rango
public int Paginas { get; set; }
[Column("FechaPublicado")] // Nombre personalizado de columna
public DateTime FechaPublicacion { get; set; }
[Precision(10, 2)] // Precisión para decimales
public decimal Precio { get; set; }
}
Configuración mediante Fluent API
La Fluent API ofrece más flexibilidad para configurar el modelo:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Configuración de la entidad Libro
modelBuilder.Entity<Libro>(entity =>
{
// Configuración de tabla
entity.ToTable("LibrosCatalogo");
// Configuración de clave primaria
entity.HasKey(e => e.Id);
// Configuración de propiedades
entity.Property(e => e.Titulo)
.IsRequired()
.HasMaxLength(200);
entity.Property(e => e.Autor)
.IsRequired()
.HasMaxLength(100)
.HasColumnType("varchar(100)");
entity.Property(e => e.Precio)
.HasPrecision(10, 2);
// Configuración de valores predeterminados
entity.Property(e => e.Disponible)
.HasDefaultValue(true);
// Configuración de índices
entity.HasIndex(e => e.Titulo)
.IsUnique();
});
}
Operaciones básicas con el contexto
Una vez configurado nuestro contexto, podemos realizar operaciones básicas de base de datos:
public class BibliotecaService
{
private readonly BibliotecaContext _context;
public BibliotecaService(BibliotecaContext context)
{
_context = context;
}
// Verificar conexión a la base de datos
public async Task<bool> VerificarConexionAsync()
{
try
{
await _context.Database.CanConnectAsync();
Console.WriteLine("Conexión exitosa a la base de datos.");
return true;
}
catch (Exception ex)
{
Console.WriteLine($"Error de conexión: {ex.Message}");
return false;
}
}
// Obtener información del esquema
public async Task MostrarInformacionEsquema()
{
var tablas = _context.Model.GetEntityTypes()
.Select(t => t.GetTableName())
.ToList();
Console.WriteLine("Tablas en la base de datos:");
foreach (var tabla in tablas)
{
Console.WriteLine($"- {tabla}");
}
// Verificar si la base de datos fue creada
var existe = await _context.Database.CanConnectAsync();
Console.WriteLine($"Estado de la base de datos: {(existe ? "Existe" : "No existe")}");
}
// Ejemplo de transacción básica
public async Task EjemploTransaccionAsync()
{
using var transaction = await _context.Database.BeginTransactionAsync();
try
{
// Múltiples operaciones en una transacción
var libro1 = new Libro
{
Titulo = "1984",
Autor = "George Orwell",
Paginas = 328,
FechaPublicacion = new DateTime(1949, 6, 8),
Precio = 18.99m
};
var libro2 = new Libro
{
Titulo = "Cien años de soledad",
Autor = "Gabriel García Márquez",
Paginas = 417,
FechaPublicacion = new DateTime(1967, 5, 30),
Precio = 22.50m
};
_context.Libros.AddRange(libro1, libro2);
await _context.SaveChangesAsync();
// Confirmar transacción
await transaction.CommitAsync();
Console.WriteLine("Transacción completada exitosamente.");
}
catch (Exception ex)
{
// Deshacer cambios en caso de error
await transaction.RollbackAsync();
Console.WriteLine($"Error en transacción: {ex.Message}");
throw;
}
}
}
Ejemplo práctico completo
Vamos a crear un ejemplo completo que demuestre el uso básico de Entity Framework Core:
using Microsoft.EntityFrameworkCore;
using BibliotecaApp.Data;
using BibliotecaApp.Models;
class Program
{
static async Task Main(string[] args)
{
Console.WriteLine("=== Introducción a Entity Framework Core ===\n");
// Configurar el contexto
var optionsBuilder = new DbContextOptionsBuilder<BibliotecaContext>();
optionsBuilder.UseSqlite("Data Source=biblioteca_ejemplo.db");
using var context = new BibliotecaContext(optionsBuilder.Options);
var servicio = new BibliotecaService(context);
try
{
// 1. Crear la base de datos
Console.WriteLine("1. Creando base de datos...");
await context.Database.EnsureCreatedAsync();
// 2. Verificar conexión
Console.WriteLine("2. Verificando conexión...");
var conectado = await servicio.VerificarConexionAsync();
if (conectado)
{
// 3. Mostrar información del esquema
Console.WriteLine("\n3. Información del esquema:");
await servicio.MostrarInformacionEsquema();
// 4. Ejemplo de transacción
Console.WriteLine("\n4. Ejecutando transacción de ejemplo...");
await servicio.EjemploTransaccionAsync();
// 5. Verificar datos insertados
Console.WriteLine("\n5. Libros en la base de datos:");
var libros = await context.Libros.ToListAsync();
if (libros.Any())
{
foreach (var libro in libros)
{
Console.WriteLine($"ID: {libro.Id}, Título: {libro.Titulo}");
Console.WriteLine($" Autor: {libro.Autor}");
Console.WriteLine($" Páginas: {libro.Paginas}, Precio: ${libro.Precio:F2}");
Console.WriteLine($" Publicado: {libro.FechaPublicacion:dd/MM/yyyy}");
Console.WriteLine($" Disponible: {(libro.Disponible ? "Sí" : "No")}\n");
}
}
else
{
Console.WriteLine("No se encontraron libros en la base de datos.");
}
}
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
Console.WriteLine("\nPresiona cualquier tecla para salir...");
Console.ReadKey();
}
}
Resumen
Entity Framework Core es un mapeador objeto-relacional potente y flexible que simplifica significativamente el trabajo con bases de datos en aplicaciones .NET. En este artículo hemos cubierto los conceptos fundamentales: qué es EF Core y sus características principales, cómo instalarlo y configurarlo, la creación de entidades y contextos, y las operaciones básicas de conexión con la base de datos.
Los conceptos clave que hemos aprendido incluyen la definición de entidades como clases de C#, la creación del DbContext como coordinador central de operaciones, la configuración del modelo mediante atributos o Fluent API, y la realización de operaciones básicas como verificación de conexión y transacciones. En el siguiente artículo profundizaremos en las operaciones CRUD (Crear, Leer, Actualizar, Eliminar) que nos permitirán manipular datos de manera efectiva utilizando Entity Framework Core.