Saltar al contenido

Mentoring

Entity Framework Core: tutorial práctico en C#

Aprende Entity Framework Core con .NET 10: DbContext, SQL Server, migraciones, LINQ, CRUD, AsNoTracking y scaffolding.

Mentoring 6 min de lectura

Actualizado el

Entity Framework Core con C#

Entity Framework Core (EF Core) es un ORM para .NET: traduce las operaciones que haces sobre objetos de C# y expresiones LINQ a consultas del proveedor de base de datos. En lugar de escribir SQL para cada operación, trabajas con clases y dejas que el ORM genere la consulta.

Además del mapeo entre tablas y objetos, EF Core centraliza el seguimiento de cambios, la gestión de la conexión y las migraciones del esquema. Eso no significa que nunca vayas a necesitar SQL ni que las consultas generadas sean óptimas por defecto: conviene revisarlas cuando el rendimiento importa.

Cuándo usar Entity Framework Core

Entity Framework Core encaja bien si quieres:

  • Mapear tablas a clases de C#.
  • Hacer consultas con LINQ.
  • Crear, modificar y eliminar registros sin escribir SQL manual.
  • Gestionar cambios de estructura con migraciones.

No siempre es la mejor opción si necesitas exprimir al máximo el rendimiento de consultas muy concretas. En esos casos puedes combinarlo con SQL directo o usar un micro ORM como Dapper. Si necesitas controlar cada consulta SQL y prefieres un micro ORM, compara este enfoque con el tutorial de Dapper en C#.

Instalar Entity Framework Core

Para seguir el tutorial necesitas .NET 10 y EF Core 10.0.12. Crea un proyecto de consola e instala los paquetes:

dotnet new console -n EfCoreTutorial -f net10.0
cd EfCoreTutorial
dotnet add package Microsoft.EntityFrameworkCore --version 10.0.12
dotnet add package Microsoft.EntityFrameworkCore.SqlServer --version 10.0.12
dotnet add package Microsoft.EntityFrameworkCore.Design --version 10.0.12
dotnet tool install --global dotnet-ef --version 10.0.12

Microsoft.EntityFrameworkCore.Tools solo es necesario si vas a usar la consola de paquetes de Visual Studio; para la CLI de dotnet ef basta con Microsoft.EntityFrameworkCore.Design. Todos los paquetes de EF Core deben mantener la misma versión principal y de parche para evitar incompatibilidades.

EF Core 10 requiere .NET 10: no funciona sobre .NET 6 ni sobre versiones anteriores.

Si prefieres crear el proyecto desde Visual Studio, puedes hacerlo desde la interfaz gráfica. Las capturas pueden mostrar una interfaz anterior, pero la plataforma de destino que debes seleccionar es net10.0:

Crear proyecto en Visual Studio

Escoge el nombre y la ubicación del proyecto:

Configurar nombre del proyecto

Selecciona la plataforma de destino (.NET 10):

Seleccionar plataforma de destino

Crear un modelo

Un modelo representa una tabla.

public class Producto
{
    public int Id { get; set; }
    public string Nombre { get; set; } = string.Empty;
    public decimal Precio { get; set; }
    public int Stock { get; set; }
}

Crear el DbContext

El DbContext representa la conexión con la base de datos y expone las tablas como DbSet.

using Microsoft.EntityFrameworkCore;

public class AppDbContext : DbContext
{
    public AppDbContext()
    {
    }

    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
    {
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        if (!optionsBuilder.IsConfigured)
        {
            var connectionString = Environment.GetEnvironmentVariable("SQLSERVER_CONNECTION_STRING");
            if (string.IsNullOrWhiteSpace(connectionString))
            {
                throw new InvalidOperationException(
                    "Define la variable de entorno SQLSERVER_CONNECTION_STRING antes de usar AppDbContext.");
            }

            optionsBuilder.UseSqlServer(connectionString);
        }
    }

    public DbSet<Producto> Productos => Set<Producto>();
}

Configurar la conexión

En la aplicación de consola del tutorial

Define SQLSERVER_CONNECTION_STRING en la terminal antes de ejecutar los comandos de migración. Por ejemplo, en Bash:

export SQLSERVER_CONNECTION_STRING="Server=localhost;Database=Tienda;Trusted_Connection=True;TrustServerCertificate=True;"
dotnet ef migrations add CrearTablaProductos
dotnet ef database update

Trusted_Connection=True usa autenticación integrada con la identidad del proceso que ejecuta la aplicación o la CLI (dotnet ef); esa identidad necesita permisos en SQL Server y el servidor debe ser accesible.

El constructor sin parámetros y OnConfiguring permiten que la CLI cree AppDbContext y use esa variable de entorno. Para trabajar con el contexto desde la consola:

using var context = new AppDbContext();

En una aplicación ASP.NET Core

En appsettings.json puedes guardar la cadena de conexión:

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=localhost;Database=Tienda;Trusted_Connection=True;TrustServerCertificate=True;"
  }
}

Registra el contexto mediante inyección de dependencias:

builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));

Esta configuración por petición encaja en una API ASP.NET Core como la utilizada en el CRUD con Blazor WebAssembly; los componentes Blazor interactivos de servidor requieren considerar IDbContextFactory. appsettings.json y builder.Services no se configuran automáticamente en una aplicación de consola.

Crear migraciones

Una migración genera los cambios necesarios en la base de datos a partir de tus modelos.

dotnet ef migrations add genera la migración y dotnet ef database update la aplica a la base de datos. Para despliegues, es preferible revisar el SQL y generar un script con dotnet ef migrations script --idempotent antes que ejecutar migraciones a ciegas en producción.

Consultar datos

A partir de aquí, los ejemplos de CRUD asumen que ya tienes un AppDbContext context creado (como en la consola) o recibido por inyección de dependencias en ASP.NET Core, además del using Microsoft.EntityFrameworkCore; necesario para los métodos asíncronos.

var productos = await context.Productos
    .OrderBy(p => p.Nombre)
    .ToListAsync();

foreach (var producto in productos)
{
    Console.WriteLine(producto.Nombre);
}

Con filtro:

var productosConStock = await context.Productos
    .Where(p => p.Stock > 0)
    .OrderBy(p => p.Nombre)
    .ToListAsync();

Insertar un registro

var producto = new Producto
{
    Nombre = "Teclado",
    Precio = 39.99m,
    Stock = 10
};

context.Productos.Add(producto);
await context.SaveChangesAsync();

SaveChangesAsync es lo que envía realmente el cambio a la base de datos.

Actualizar un registro

var producto = await context.Productos.FirstOrDefaultAsync(p => p.Id == 1);

if (producto != null)
{
    producto.Precio = 34.99m;
    await context.SaveChangesAsync();
}

Entity Framework detecta que el objeto cambió y genera el UPDATE correspondiente.

Eliminar un registro

var producto = await context.Productos.FirstOrDefaultAsync(p => p.Id == 1);

if (producto != null)
{
    context.Productos.Remove(producto);
    await context.SaveChangesAsync();
}

Obtener datos sin seguimiento

Si solo vas a leer datos y no vas a modificarlos con ese contexto, usa AsNoTracking para reducir el trabajo interno.

var productos = await context.Productos
    .AsNoTracking()
    .Where(p => p.Stock > 0)
    .ToListAsync();

Scaffolding desde una base de datos existente

Si ya tienes una base de datos creada, puedes generar modelos y contexto con dotnet ef dbcontext scaffold:

dotnet ef dbcontext scaffold \
  "Server=localhost;Database=AdventureWorks;Trusted_Connection=True;TrustServerCertificate=True;" \
  Microsoft.EntityFrameworkCore.SqlServer \
  -o Models \
  -t Producto \
  --context-dir Context \
  -c AppDbContext
  • -o Models: carpeta de salida para las clases generadas.
  • -t Producto: genera solo la tabla indicada; puedes repetir -t para varias tablas u omitirlo para generar todas.
  • --context-dir Context: carpeta donde se guarda el DbContext.
  • -c AppDbContext: nombre de la clase de contexto.

Si trabajas desde la consola de paquetes de Visual Studio, dispones del comando equivalente Scaffold-DbContext.

Recomendaciones

  • No guardes cadenas de conexión ni credenciales en el código fuente ni en el repositorio.
  • Usa migraciones para cambios de estructura si el proyecto lo permite.
  • Usa AsNoTracking en consultas de solo lectura.
  • Proyecta solo las columnas que necesitas en lugar de cargar entidades completas.
  • Revisa el SQL generado y los logs si una consulta va lenta.
  • Si recurres a SQL directo, usa consultas parametrizadas.

Entity Framework Core simplifica mucho el CRUD habitual, pero conviene entender qué consulta genera y cuándo merece la pena optimizarla.