En Entity Framework, podemos implementar paginación fácilmente usando los métodos Skip()
y Take()
.
En aplicaciones que manejan grandes volúmenes de datos, recuperar todos los registros de una base de datos en una sola consulta puede no ser viable, porque costaría demasiado tiempo.
Por ejemplo, si cargamos 10.000 productos de un catálogo, sería muy lento. Además, el usuario no necesita (ni puede) ver 10.000 productos a la vez.
La paginación nos permite dividir los resultados en fragmentos manejables. Esto mejora la experiencia del usuario y, además, reduce el consumo de recursos.
¿Qué son Skip
y Take
?
Estos métodos pertenecen a LINQ y se utilizan para:
Skip(n)
: Omite los primerosn
elementos de una consulta.Take(m)
: Selecciona los siguientesm
elementos después de saltar.
Juntos, permiten implementar paginación de forma muy sencilla.
Por ejemplo, si tenemos una tabla Productos
con 100 registros y queremos mostrar 10 por página, la segunda página se obtendría con:
var pagina2 = dbContext.Productos
.OrderBy(p => c.Id)
.Skip(10) // Salta los primeros 10
.Take(10) // Toma los siguientes 10
.ToList();
Implementación básica de paginación
Supongamos que queremos paginar una lista de clientes en una aplicación web. El código sería:
public List<Cliente> ObtenerClientesPaginados(int pagina, int registrosPorPagina)
{
using (var context = new AppDbContext())
{
return context.Clientes
.OrderBy(c => c.Nombre) // ¡Importante ordenar!
.Skip((pagina - 1) * registrosPorPagina)
.Take(registrosPorPagina)
.ToList();
}
}
(pagina - 1) * registrosPorPagina
calcula cuántos registros saltar.- Si
pagina = 1
,Skip(0)
devuelve desde el primer registro.
Cálculo del número total de páginas
Para implementar una paginación completa, suele ser necesario calcular el número total de páginas disponibles.
Esto se puede hacer dividiendo el número total de registros por el tamaño de la página:
public (List<Cliente> Datos, int TotalPaginas) GetClientesPaginados(int pagina, int porPagina)
{
using (var context = new AppDbContext())
{
var total = context.Clientes.Count();
var totalPaginas = (int)Math.Ceiling(total / (double)porPagina);
var datos = context.Clientes
.OrderBy(c => c.Nombre)
.Skip((pagina - 1) * porPagina)
.Take(porPagina)
.ToList();
return (datos, totalPaginas);
}
}
Este valor se puede utilizar para mostrar controles de paginación en la interfaz de usuario, como botones de “Anterior” y “Siguiente”.
Evitar Skip
en grandes desplazamientos
En tablas con muchos (millones) de registros, Skip()
puede ser lento. Esto se debe a que Skip
debe recorrer todos los registros anteriores antes de omitirlos.
Para optimizar el rendimiento en estos casos, se pueden utilizar técnicas como:
- Índices en columnas de ordenamiento: Asegúrate de que las columnas utilizadas en
OrderBy
estén indexadas. - Paginación basada en claves: En lugar de usar
Skip
, utiliza una condiciónWhere
basada en el último valor de la página anterior.
Por ejemplo, si estamos paginando productos por su ID:
int ultimoId = 10; // Último ID de la página anterior
int tamañoPagina = 10;
var productos = context.Productos
.Where(p => p.Id > ultimoId)
.OrderBy(p => p.Id)
.Take(tamañoPagina)
.ToList();