Considere estos entidad artificial objetos:Extracción de selección de N + 1 sin .include
public class Consumer
{
public int Id { get; set; }
public string Name { get; set; }
public bool NeedsProcessed { get; set; }
public virtual IList<Purchase> Purchases { get; set; } //virtual so EF can lazy-load
}
public class Purchase
{
public int Id { get; set; }
public decimal TotalCost { get; set; }
public int ConsumerId { get; set; }
}
Ahora vamos a decir que desea ejecutar este código:
var consumers = Consumers.Where(consumer => consumer.NeedsProcessed);
//assume that ProcessConsumers accesses the Consumer.Purchases property
SomeExternalServiceICannotModify.ProcessConsumers(consumers);
Por defecto, este sufrirá de Selección N + 1 dentro del método ProcessConsumers. Que dará lugar a una consulta cuando enumera los consumidores, entonces va a agarrar cada colección compras 1 por 1. La solución estándar para este problema sería añadir un include:
var consumers = Consumers.Include("Purchases").Where(consumer => consumer.NeedsProcessed);
//assume that ProcessConsumers accesses the Consumer.Purchases property
SomeExternalServiceICannotModify.ProcessConsumers(consumers);
Eso funciona bien en muchos casos, pero en algunos casos complejos, una inclusión puede destruir por completo el rendimiento en órdenes de magnitud. ¿Es posible hacer algo como esto:.
- agarrar mis consumidores, consumidores var = _entityContext.Consumers.Where (...) ToList()
- agarrar mis compras, compras var = _entityContext.Purchases. Dónde (...). ToList()
- Hidrata al consumidor. Compra colecciones manualmente de las compras que ya cargué en la memoria. Luego, cuando lo paso a ProcessConsumers, no generará más consultas de db.
No sé cómo hacer # 3. Si intenta acceder a cualquier consumidor. Compra una colección que activará la carga diferida (y, por lo tanto, la Select N + 1). Tal vez deba enviar los consumidores al tipo correcto (en lugar del tipo de proxy EF) y luego cargar la colección? Algo como esto:
foreach (var consumer in Consumers)
{
//since the EF proxy overrides the Purchases property, this doesn't really work, I'm trying to figure out what would
((Consumer)consumer).Purchases = purchases.Where(x => x.ConsumerId = consumer.ConsumerId).ToList();
}
EDIT: he vuelto a escribir un poco el ejemplo de esperar para revelar el problema con mayor claridad.
IIRC EF hidratará automáticamente las colecciones, por lo que el n. ° 3 no tiene que hacerse manualmente. – jeroenh
Su primera consulta debería ejecutarse como una sola instrucción de SQL. ¿Estás viendo múltiples llamadas a db? –
@Nicholas, tienes razón, actualicé el ejemplo para hacerlo Seleccionar N + 1. Este es un ejemplo artificial muy simple, lea toda la pregunta e intente comprender lo que realmente estoy preguntando. Los ejemplos reales donde .Include es insuficiente son dramáticamente más complejos y no es razonable incluirlos en una pregunta de SO. – manu08