Short version:
Starting with a List of entities and needing all dependent entities through an association, is there a way to use the corresponding navigation-propertiy to load all child-entities with one db-round-trip? Ie. generate a single WHERE fkId IN (...)
statement via navigation property?
Long version:
I am generating a report from a highly normalised database with millions of rows. The report will be centered around a few rows from one table, with several sets of child-rows from related table-hierarchies to calculate the numbers. I have yet to find a good way of loading the data. I've found these so far:
Keep the main-set of entities as IQueriable<T>
- Not good since the db will have to find the main set every time and join to get the requested data.
Put the main-set into an array or list, then get related data through navigation properties
- This is slow since it will generate a select for every main-entity.
Put the Ids of the main entities into myIdsArray then filter the entire dependent-ObjectSet with "where myIdsArray.Contains(foreignKeyId)". Linq then generates a "WHERE foreignKeyId IN (...)"-clause.
- This is fast but only possible for 1:*-relations since linking-tables are mapped away.
- Removes the readablity advantage of EF by using Ids after all
- The navigation-properties of type EntityCollection<T> are not populated
Eager loading though the .Include()-methods, included for completeness despite asking for lazy-loading
- Alledgedly joins everything included together and returns one giant flat result.
- Have to decide up front which data to use
It there some way to get the simplicity of 2 with the performance of 3? Something like
var products = customer.**SelectManyAndLoadInOneGo**(c => c.Products) ?
Here is an example of 2 and 3 using the Nutshell.mdf in linqPad's join-examples:
var customers = Customers.Where(c => c.Name.Length == 4)
.ToArray() // Find the customers only once in the db
.Dump("1 SELECT * FROM Customer WHERE ...");
var purchases = customers.SelectMany(c => c.Purchases)
.Dump("N SELECT * FROM Products WHERE CustomerID = @p0");
var customerIds = customers.Select(c => c.ID).ToArray();
var purchases2 = Purchases.Where(p => customerIds.Contains((int)p.CustomerID))
.Dump("1 SELECT * FROM Products WHERE CustomerID IN (@p0, @p1, ...)");