Удаление select N+1 без .Включить

Рассмотрим эти надуманные объекты сущностей:

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; }
}

Теперь предположим, что я хочу запустить этот код:

var consumers = Consumers.Where(consumer => consumer.NeedsProcessed);

//assume that ProcessConsumers accesses the Consumer.Purchases property
SomeExternalServiceICannotModify.ProcessConsumers(consumers);

По умолчанию это будет зависеть от Select N+1 внутри метода ProcessConsumers. Он вызовет запрос, когда перечислит потребителей, а затем захватит каждую коллекцию покупок 1 на 1. Стандартным решением этой проблемы было бы добавить include:

var consumers = Consumers.Include("Purchases").Where(consumer => consumer.NeedsProcessed);

//assume that ProcessConsumers accesses the Consumer.Purchases property
SomeExternalServiceICannotModify.ProcessConsumers(consumers);

Это прекрасно работает во многих случаях, но в некоторых сложных случаях включение может полностью разрушить производительность на порядки. Можно ли сделать что-то вроде этого:

  1. Захватите моих потребителей, var consumers = _entityContext.Consumers.Where(...). ToList()
  2. Захват моих покупок, var покупок = _entityContext.Покупки.Где(...). ToList()
  3. Увлажнение потребителя.Покупает коллекции вручную из покупок, которые я уже загрузил в память. Затем, когда я передаю его в ProcessConsumers, он не будет вызывать больше запросов к базе данных.

Я не знаю, как сделать #3. Если вы попытаетесь получить доступ к любому потребителю. Коллекция покупок, которая вызовет ленивую загрузку (и, следовательно, Select N+1). Возможно, мне нужно привести Consumers к правильному типу (вместо типа прокси-сервера EF), а затем загрузить коллекцию? Что-то вроде этого:

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();
}

ПРАВКА: Я немного переписал пример, чтобы, надеюсь, раскрыть проблему более четко.

7
задан manu08 9 June 2012 в 19:19
поделиться