0Pricing
C# Academy · Lección

Consultas compiladas y rendimiento

Mejore el rendimiento de las consultas mediante EF.CompileQuery, AsNoTracking y proyecciones con Select.

Consultas compiladas y rendimiento es una lección gratuita de C# Academy en CoddyKit. Esta es la lección 3 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de C# Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de C# Academy incluye 4 lecciones en total.

Compilación de consultas de EF Core

Cada vez que EF Core ejecuta una consulta de LINQ, debe analizar el árbol de expresiones, traducirlo a SQL y validarlo. En las rutas de ejecución frecuentes, esta sobrecarga se acumula. EF Core ofrece varias herramientas para minimizarla.

AsNoTracking para consultas de solo lectura

AsNoTracking() omite el registro en el seguimiento de cambios. En consultas de solo lectura (respuestas de API, informes), esta es la mejora sencilla más importante: normalmente ofrece una velocidad entre 2 y 5 veces mayor y reduce el uso de memoria.

// With tracking (default) — for entities you'll modify
var tracked = await _db.Products.FirstOrDefaultAsync(p => p.Id == 1);

// Without tracking — for read-only
var dto = await _db.Products
    .AsNoTracking()
    .Where(p => p.IsActive)
    .Select(p => new ProductDto { Id = p.Id, Name = p.Name })
    .ToListAsync();

Consultas compiladas

EF.CompileAsyncQuery precompila una consulta de LINQ en un delegado. La traducción se realiza una vez al iniciar la aplicación; las llamadas posteriores omiten por completo el paso de compilación.

// Define once, reuse many times
private static readonly Func<AppDbContext, int, Task<Product?>> GetProductById =
    EF.CompileAsyncQuery(
        (AppDbContext db, int id) =>
            db.Products.AsNoTracking().FirstOrDefault(p => p.Id == id));

// Usage
var product = await GetProductById(_db, 42);

Proyección con Select para mejorar el rendimiento

Proyecte siempre exactamente los campos que necesita. Seleccionar un DTO en lugar de una entidad completa reduce las columnas obtenidas, la carga útil de red y la presión sobre el recolector de basura.

// Load entire entity (over-fetch)
var all = await _db.Products.ToListAsync();

// Projection: only fetch needed columns
var names = await _db.Products
    .AsNoTracking()
    .Select(p => new { p.Id, p.Name, p.Price })
    .ToListAsync();
// SQL: SELECT Id, Name, Price FROM Products

Sugerencias de índices y optimización de consultas sin formato

EF Core respeta los índices de base de datos que configure con HasIndex(). Los índices compuestos, los índices filtrados y las columnas incluidas afectan al rendimiento del plan de consulta.

// Configure compound index in OnModelCreating
modelBuilder.Entity<Order>()
    .HasIndex(o => new { o.CustomerId, o.Status })
    .HasFilter("[Status] = 0")  // filtered index
    .IncludeProperties(o => o.PlacedAt);

// The query below will use this index efficiently
var pending = await _db.Orders
    .AsNoTracking()
    .Where(o => o.CustomerId == 5 && o.Status == OrderStatus.Pending)
    .ToListAsync();

Paginación con Skip y Take

Pagine siempre los conjuntos de resultados grandes. Skip y Take se traducen a SQL OFFSET/FETCH y evitan cargar miles de filas en memoria.

int page = 2, pageSize = 25;

var products = await _db.Products
    .AsNoTracking()
    .Where(p => p.IsActive)
    .OrderBy(p => p.Name)
    .Skip((page - 1) * pageSize)
    .Take(pageSize)
    .ToListAsync();
// SQL: ... ORDER BY Name OFFSET 25 ROWS FETCH NEXT 25 ROWS ONLY

Recuento eficiente

Utilice CountAsync() o AnyAsync() en lugar de cargar registros solo para contarlos. AnyAsync() es incluso más rápido que COUNT cuando solo necesita comprobar la existencia.

// Total count for pagination
int totalCount = await _db.Products
    .Where(p => p.IsActive)
    .CountAsync();

// Existence check — faster than Count > 0
bool hasExpensive = await _db.Products
    .AnyAsync(p => p.Price > 1000m);
// SQL: IF EXISTS (SELECT 1 FROM ...) SELECT 1

Inserciones masivas con AddRange

Utilice AddRange para añadir varias entidades a la vez. EF Core agrupa las instrucciones INSERT en menos viajes de ida y vuelta, controlados por MaxBatchSize en el proveedor.

var newProducts = Enumerable.Range(1, 1000)
    .Select(i => new Product { Name = $"Product {i}", Price = i * 1.5m })
    .ToList();

_db.Products.AddRange(newProducts);
await _db.SaveChangesAsync();
// Batched: typically 100-1000 rows per round-trip

EF.Functions para funciones de base de datos

EF.Functions expone funciones específicas de la base de datos (LIKE, partes de fecha y búsqueda de texto completo) que EF Core traduce a SQL en lugar de evaluarlas en memoria.

// LIKE pattern search (SQL-side)
var results = await _db.Products
    .Where(p => EF.Functions.Like(p.Name, "%keyboard%"))
    .AsNoTracking()
    .ToListAsync();

// Date difference (SQL-side)
var recentOrders = await _db.Orders
    .Where(o => EF.Functions.DateDiffDay(o.PlacedAt, DateTime.UtcNow) <= 7)
    .ToListAsync();

Diagnóstico de consultas lentas

Habilite el registro de consultas de EF Core para ver el SQL generado. Utilice EnableSensitiveDataLogging() en desarrollo para ver los valores de los parámetros.

builder.Services.AddDbContext<AppDbContext>(opt =>
    opt.UseSqlServer(connStr)
       .LogTo(Console.WriteLine, LogLevel.Information)
       .EnableSensitiveDataLogging() // dev only!
       .EnableDetailedErrors());     // dev only!

En la práctica: endpoint de lista optimizado

Un endpoint de lista listo para producción combina AsNoTracking, proyección, índices y paginación para obtener el máximo rendimiento.

app.MapGet("/products", async (
    AppDbContext db,
    string? search,
    int page = 1, int size = 20) =>
{
    var query = db.Products
        .AsNoTracking()
        .Where(p => p.IsActive);

    if (!string.IsNullOrEmpty(search))
        query = query.Where(p => EF.Functions.Like(p.Name, $"%{search}%"));

    var total = await query.CountAsync();
    var items = await query
        .OrderBy(p => p.Name)
        .Skip((page - 1) * size).Take(size)
        .Select(p => new { p.Id, p.Name, p.Price })
        .ToListAsync();

    return Results.Ok(new { total, items });
});

Comprobación rápida

¿Cuál es la principal ventaja de EF.CompileAsyncQuery frente a una consulta de LINQ normal?

Resumen: consultas compiladas y rendimiento

Conclusiones clave:

  • AsNoTracking(): omite el seguimiento de cambios en consultas de solo lectura — la mejora sencilla más rápida
  • EF.CompileAsyncQuery: precompila las consultas frecuentes en delegados
  • Proyección con Select: obtiene solo las columnas necesarias
  • Los índices adecuados (HasIndex) mejoran notablemente los planes de consulta
  • Utilice paginación con Skip/Take; use AnyAsync para comprobar la existencia
  • Habilite el registro de consultas durante el desarrollo para detectar SQL lento o inesperado

Preguntas frecuentes

¿La lección «Consultas compiladas y rendimiento» es gratis?

Sí — el texto completo de «Consultas compiladas y rendimiento» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de C# Academy, actualiza a CoddyKit PRO. El curso de C# Academy incluye 4 lecciones en total.

¿Qué aprenderé en «Consultas compiladas y rendimiento»?

Mejore el rendimiento de las consultas mediante EF.CompileQuery, AsNoTracking y proyecciones con Select. Practicas C# Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar C# Academy?

No se requiere experiencia previa. C# Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 3 de 4.

¿Cuánto tiempo toma la lección «Consultas compiladas y rendimiento»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de C# Academy?

Sí. Cada lección de C# Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Carga eager, lazy y explícita
  2. SQL sin formato, procedimientos almacenados e interpolación
  3. Consultas compiladas y rendimiento
  4. Filtros de consulta globales y entidades propias
← Volver a C# Academy