SqlQuery
poses problems some of which I wasn't yet aware of.
No Include
You correctly explain why Include
doesn't have any effect after you convert an IEnumerable
to an IQueryable
. Include
extends an expression, and an IEnumerable
doesn't have an expression (and doesn't magically get one after converting it to IQueryable
).
No lazy loading without attaching
So you end up having a list of Product
s not attached to the context and not capable of lazy loading. You have to attach the Product
s to the context explicitly (e.g. by setting their state to Unchanged
) to make them lazy loading.
This is something I just found out and which is odd. The entities are materialized as proxy objects, but, contrary to detached entities that are fetched by DbSet.AsNoTracking()
, they don't display lazy loading. I think it's a bug and I reported it.
But lazy loading causes the n + 1 query anti-pattern: queries that fetch Inventory objects for each Product separately. So it's not the best way to get your object graph.
No relationship fixup
So what you really want is load all Inventory objects that belong to these products in one shot and let relationship fixup do its job (i.e. EF auto-fills all Product.Inventories
collections):
context.Inventories.Where(i => productIds.Contains(i.ProductId)).Load();
...where productIds
is a list of procuct Ids in p
.
But relationship fixup only works for attached entities, so again, you'll have to change their state to Unchanged
explicitly. If you do that, you're ready to go... except you aren't.
Again there's a difference with detached entities fetched by DbSet.AsNoTracking()
. The latter respond to relationship fixup, the ones from SqlQuery
don't!
(Rather) elaborate solution
So what's left is: load the related Inventory
objects as show above and fill the Product.Inventories
collections yourself:
var query = from product in p
join inv context.Inventories.Local
on product.ProductId equals inv.ProductId
into inventories
select new { product, inventories }
foreach(var anon in query)
{
anon.product.Inventories = anon.inventories.ToList();
}
Of course this is all quite unsatisfactory.