En Entity Framework, podemos controlar cómo queremos cargar los datos relacionados con una entidad.
En una base de datos relacional, las entidades están relacionadas entre sí mediante claves primarias y foráneas.
La carga de datos relacionados se refiere a cómo y cuándo se recuperan los datos de las entidades relacionadas en una consulta.
Por ejemplo, en una base de datos de una tienda en línea, podríamos tener una entidad Pedido
que está relacionada con una entidad Cliente
.
Cuando recuperamos un Pedido
, es posible que también queramos obtener los datos del Cliente
asociado.
Es muy frecuente que queramos obtener estos datos relacionados (de hecho, lo haremos contínuamente).
Sin embargo, la forma en la que lo hagamos puede tener un gran impacto en la velocidad y rendimiento de nuestra aplicación ⚡.
Estrategias de carga de datos relacionados
Entity Framework nos permite cargar las entidades relacionadas de tres maneras diferentes:
- Eager Loading: Carga todas las entidades relacionadas de una sola vez, en la misma consulta.
- Lazy Loading: Carga las entidades relacionadas solo cuando se accede a ellas.
- Explicit Loading: Carga las entidades relacionadas manualmente, cuando el desarrollador lo decide.
Como no podría ser de otra forma, cada una tiene sus ventajas y desventajas, y un escenario adecuado donde conviene usar una u otra.
Vamos a ver cada una de las estrategias en detalle.
Carga Eager
Eager Loading es una estrategia en la que todas las entidades relacionadas se cargan de una sola vez, junto con la entidad principal. Esto se logra utilizando el método Include
en nuestras consultas LINQ.
Supongamos que tenemos las siguientes entidades:
public class Cliente
{
public int ClienteId { get; set; }
public string Nombre { get; set; }
public ICollection<Pedido> Pedidos { get; set; }
}
public class Pedido
{
public int PedidoId { get; set; }
public DateTime Fecha { get; set; }
public int ClienteId { get; set; }
public Cliente Cliente { get; set; }
}
Si queremos recuperar un Cliente
junto con todos sus Pedidos
, podemos usar Eager Loading de la siguiente manera:
using (var context = new MiContexto())
{
var cliente = context.Clientes
.Include(c => c.Pedidos)
.FirstOrDefault(c => c.ClienteId == 1);
}
En este ejemplo, Entity Framework generará una consulta SQL que recupera tanto el Cliente
como sus Pedidos
relacionados en una sola consulta.
Lazy Loading
Lazy Loading es una estrategia en la que las entidades relacionadas se cargan solo cuando se accede a ellas por primera vez. Esto significa que, inicialmente, solo se carga la entidad principal, y las entidades relacionadas se cargan bajo demanda.
Para habilitar Lazy Loading en Entity Framework, debemos asegurarnos de que nuestras propiedades de navegación estén marcadas como virtual
:
public class Cliente
{
public int ClienteId { get; set; }
public string Nombre { get; set; }
public virtual ICollection<Pedido> Pedidos { get; set; }
}
Luego, podemos recuperar un Cliente
sin cargar sus Pedidos
de inmediato:
using (var context = new MiContexto())
{
var cliente = context.Clientes
.FirstOrDefault(c => c.ClienteId == 1);
// Los Pedidos se cargarán solo cuando se acceda a ellos
var pedidos = cliente.Pedidos;
}
En este caso, Entity Framework generará una consulta SQL para recuperar el Cliente
, y luego, cuando accedamos a cliente.Pedidos
, generará otra consulta para recuperar los Pedidos
relacionados.
Explicit Loading
Explicit Loading es una estrategia en la que el desarrollador decide cuándo cargar las entidades relacionadas. Esto se hace manualmente utilizando el método Load
en el DbContext
.
Supongamos que queremos cargar los Pedidos
de un Cliente
solo cuando lo decidamos:
using (var context = new MiContexto())
{
var cliente = context.Clientes
.FirstOrDefault(c => c.ClienteId == 1);
// Cargamos los Pedidos explícitamente
context.Entry(cliente)
.Collection(c => c.Pedidos)
.Load();
}
En este ejemplo, Entity Framework generará una consulta SQL para recuperar el Cliente
, y luego, cuando llamemos a Load
, generará otra consulta para recuperar los Pedidos
relacionados.
¿Cuándo usar cada estrategia?
La pregunta más importante, es cuando usar cada una de estas estrategias. Empecemos viendo resumen de cada una de las estrategias.
Característica | Eager Loading | Lazy Loading | Explicit Loading |
---|---|---|---|
Carga de datos | Todos los datos relacionados se cargan de una vez | Los datos relacionados se cargan bajo demanda | Los datos relacionados se cargan manualmente |
Número de consultas | Una sola consulta | Múltiples consultas (N+1) | Múltiples consultas (bajo control) |
Rendimiento | Mejor cuando se necesitan todos los datos | Mejor cuando se accede a pocos datos relacionados | Mejor cuando se necesita control total |
Complejidad del código | Simple | Simple | Más complejo |
Ahora, veamos cuando conviene usar cada una de ellas, con sus ventajas y desventajas.
Eager Loading
Útil cuando sabemos que vamos a necesitar todos los datos relacionados desde el principio.
Por ejemplo, en una pantalla de detalles donde se muestran todos los datos de una entidad y sus relaciones.
- Reducción de consultas a la base de datos: Al cargar todas las entidades relacionadas de una sola vez, se minimiza el número de consultas a la base de datos.
- Mejor rendimiento en ciertos escenarios: Si sabemos que vamos a necesitar los datos relacionados, Eager Loading puede ser más eficiente que hacer múltiples consultas.
- Sobrecarga de datos: Si cargamos más datos de los necesarios, podemos terminar con un exceso de información en memoria, lo que puede afectar el rendimiento.
Lazy Loading
Útil cuando no estamos seguros de si necesitaremos los datos relacionados, o cuando solo accederemos a ellos en ciertas condiciones.
Por ejemplo, en una lista de entidades donde solo se cargan los detalles al hacer clic en un elemento.
- Carga bajo demanda: Solo se cargan los datos cuando realmente se necesitan, lo que puede ahorrar memoria y tiempo de ejecución.
- Simplicidad en el código: No es necesario especificar qué entidades relacionadas cargar, lo que simplifica el código.
- Problemas de rendimiento: Si accedemos a muchas entidades relacionadas, podemos terminar con un gran número de consultas a la base de datos, lo que se conoce como el problema de “N+1 consultas”.
Explicit Loading
Útil cuando necesitamos un control total sobre cuándo y qué datos relacionados cargar.
Por ejemplo, en una aplicación compleja donde el rendimiento es crítico y queremos evitar cargas innecesarias.
- Control total: El desarrollador tiene control total sobre cuándo y qué entidades relacionadas cargar.
- Evita el problema de N+1 consultas: Al cargar las entidades relacionadas manualmente, podemos evitar el problema de N+1 consultas que ocurre con Lazy Loading.
- Código más complejo: El código puede volverse más verboso y complejo, ya que debemos manejar manualmente la carga de las entidades relacionadas.
- Mayor riesgo de errores: Al tener que manejar manualmente la carga de entidades, hay un mayor riesgo de errores y olvidos.
Mejores prácticas
Aunque Eager Loading puede ser eficiente, usarlo en exceso o con relaciones muy grandes puede resultar en consultas demasiado y descargar demasiados datos.
Por contra, con Lazy Loading puedes tener un problema de N+1 consultas ( ocurre cuando EF Core realiza una consulta adicional por cada entidad cargada). Asegúrate de no cargar relaciones innecesarias de forma implícita.