Consider these far-fetched entity objects:
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; } }
Now let's say that I want to run this code:
var consumers = Consumers.Where(consumer => consumer.NeedsProcessed);
By default, this will be caused by selecting N + 1 inside the ProcessConsumers method. It will trigger a query when it lists consumers, then it will grab every 1 to 1 shopping collection. A standard solution to this problem would be to include include:
var consumers = Consumers.Include("Purchases").Where(consumer => consumer.NeedsProcessed);
This works well in many cases, but in some complex cases, inclusion can completely destroy performance by orders of magnitude. Is it possible to do something like this:
- Grab my consumers, var customers = _entityContext.Consumers.Where (...). ToList ()
- Grab my purchases, buy var = _entityContext.Purchases.Where (...). ToList ()
- Consumer hydrate. Acquires collections manually from purchases that I have already loaded into memory. Then, when I pass it to ProcessConsumers, it will not trigger any more db requests.
I'm not sure how to do # 3. If you try to access any consumer.Purchases collection that will cause a lazy load (and therefore Select N + 1). Perhaps I need to give consumers the correct type (instead of the proxy type EF), and then download the collection? Something like that:
foreach (var consumer in Consumers) {
EDIT: I rewrote the example a bit to hopefully show the problem more clearly.
manu08
source share