views:

24

answers:

0

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:

  1. 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.
  2. 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.
  3. 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
  4. 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, ...)");