Everything in life is about choices and usually we have to evaluate our options before we make those choices. So it goes with eager loading vs. deferred loading in Entity Framework.
We have the option of eager loading with the Include method and can use it very creatively.
For an entity that has a lot of relationships you could do something like:
context.Customers.Include("Orders.LineItems.Product") .Include("Addresses")
and retrieve a very rich Customer graph with only a single hit to the database.
There's another way to eager load as well -- using projection:
var custs = from cust in context.Customers from order in cust.Orders from lineitem in order.LineItems select new { cust, cust.Orders, order.LineItems, lineitem.Product};
foreach (var item in custs){Customer cust = item.cust;
}
When this query is executed, all of the entities will be joined in graphs as they are retrieved. So that cust in the enumeration will already have all of it's Orders with LineItems and Products attached.
You could also defer the loading of the related objects, thereby querying for just the customers and then loading more data later.
You might choose this route if you need to make some decisions about whether or n ot you really want that data. This example is a little bit exaggerated.
ObjectQuery<Customer> custs=context.Customers; foreach (Customer c in custs) { if (IWantTheOrders()) { c.Orders.Load(); //hit the database again foreach (Order o in c.Orders) { if (IWantTheLIneItems()) o.LineItems.Load(); //hit the database again }}
Now comes the question of performance.
Let's first take a look at the store queries.
If you are sure that you want all of that data up front, then the Include is certainly the easiest thing to code.
The store query isn't pretty because all of the joins and outer joins need to be built. I have made the painful decision to paste the whole thing here. Remember that the query will actually flatten all of the data from the joins into individual rows, one for each unique customer.Order.LineItem.Product combo.
The projection query is different, but not terribly different and looks just as nasty. I'll leave it to a dba to discern if it's any better or worse than the above.
| Store Query From Includes SELECT [UnionAll1].[CustomerID] AS [C1], [UnionAll1].[NameStyle] AS [C2], [UnionAll1].[Title] AS [C3], [UnionAll1].[FirstName] AS [C4], [UnionAll1].[MiddleName] AS [C5], [UnionAll1].[LastName] AS [C6], [UnionAll1].[Suffix] AS [C7], [UnionAll1].[CompanyName] AS [C8], [UnionAll1].[SalesPerson] AS [C9], [UnionAll1].[EmailAddress] AS [C10], [UnionAll1].[Phone] AS [C11], [UnionAll1].[PasswordHash] AS [C12], [UnionAll1].[PasswordSalt] AS [C13], [UnionAll1].[rowguid] AS [C14], [UnionAll1].[ModifiedDate] AS [C15], [UnionAll1].[C2] AS [C16], [UnionAll1].[C1] AS [C17], [UnionAll1].[SalesOrderID] AS [C18], [UnionAll1].[RevisionNumber] AS [C19], [UnionAll1].[OrderDate] AS [C20], [UnionAll1].[DueDate] AS [C21], [UnionAll1].[ShipDate] AS [C22], [UnionAll1].[Status] AS [C23], [UnionAll1].[OnlineOrderFlag] AS [C24], [UnionAll1].[SalesOrderNumber] AS [C25], [UnionAll1].[PurchaseOrderNumber] AS [C26], [UnionAll1].[AccountNumber] AS [C27], [UnionAll1].[ShipMethod] AS [C28], [UnionAll1].[CreditCardApprovalCode] AS [C29], [UnionAll1].[SubTotal] AS [C30], [UnionAll1].[TaxAmt] AS [C31], [UnionAll1].[Freight] AS [C32], [UnionAll1].[TotalDue] AS [C33], [UnionAll1].[Comment] AS [C34], [UnionAll1].[rowguid1] AS [C35], [UnionAll1].[ModifiedDate1] AS [C36], [UnionAll1].[C3] AS [C37], [UnionAll1].[BillToAddressID] AS [C38], [UnionAll1].[ShipToAddressID] AS [C39], [UnionAll1].[CustomerID1] AS [C40], [UnionAll1].[C4] AS [C41], [UnionAll1].[C5] AS [C42], [UnionAll1].[SalesOrderID1] AS [C43], [UnionAll1].[SalesOrderDetailID] AS [C44], [UnionAll1].[OrderQty] AS [C45], [UnionAll1].[UnitPrice] AS [C46], [UnionAll1].[UnitPriceDiscount] AS [C47], [UnionAll1].[LineTotal] AS [C48], [UnionAll1].[rowguid2] AS [C49], [UnionAll1].[ModifiedDate2] AS [C50], [UnionAll1].[C6] AS [C51], [UnionAll1].[ProductID] AS [C52], [UnionAll1].[Name] AS [C53], [UnionAll1].[ProductNumber] AS [C54], [UnionAll1].[Color] AS [C55], [UnionAll1].[StandardCost] AS [C56], [UnionAll1].[ListPrice] AS [C57], [UnionAll1].[Size] AS [C58], [UnionAll1].[Weight] AS [C59], [UnionAll1].[SellStartDate] AS [C60], [UnionAll1].[SellEndDate] AS [C61], [UnionAll1].[DiscontinuedDate] AS [C62], [UnionAll1].[ThumbNailPhoto] AS [C63], [UnionAll1].[ThumbnailPhotoFileName] AS [C64], [UnionAll1].[rowguid3] AS [C65], [UnionAll1].[ModifiedDate3] AS [C66], [UnionAll1].[ProductCategoryID] AS [C67], [UnionAll1].[ProductModelID] AS [C68], [UnionAll1].[C7] AS [C69], [UnionAll1].[C12] AS [C70], [UnionAll1].[C11] AS [C71], [UnionAll1].[C8] AS [C72], [UnionAll1].[C9] AS [C73], [UnionAll1].[C10] AS [C74] FROM (SELECT [Project2].[C2] AS [C1], [Extent1].[CustomerID] AS [CustomerID], [Extent1].[NameStyle] AS [NameStyle], [Extent1].[Title] AS [Title], [Extent1].[FirstName] AS [FirstName], [Extent1].[MiddleName] AS [MiddleName], [Extent1].[LastName] AS [LastName], [Extent1].[Suffix] AS [Suffix], [Extent1].[CompanyName] AS [CompanyName], [Extent1].[SalesPerson] AS [SalesPerson], [Extent1].[EmailAddress] AS [EmailAddress], [Extent1].[Phone] AS [Phone], [Extent1].[PasswordHash] AS [PasswordHash], [Extent1].[PasswordSalt] AS [PasswordSalt], [Extent1].[rowguid] AS [rowguid], [Extent1].[ModifiedDate] AS [ModifiedDate], 1 AS [C2], [Project2].[SalesOrderID] AS [SalesOrderID], [Project2].[RevisionNumber] AS [RevisionNumber], [Project2].[OrderDate] AS [OrderDate], [Project2].[DueDate] AS [DueDate], [Project2].[ShipDate] AS [ShipDate], [Project2].[Status] AS [Status], [Project2].[OnlineOrderFlag] AS [OnlineOrderFlag], [Project2].[SalesOrderNumber] AS [SalesOrderNumber], [Project2].[PurchaseOrderNumber] AS [PurchaseOrderNumber], [Project2].[AccountNumber] AS [AccountNumber], [Project2].[ShipMethod] AS [ShipMethod], [Project2].[CreditCardApprovalCode] AS [CreditCardApprovalCode], [Project2].[SubTotal] AS [SubTotal], [Project2].[TaxAmt] AS [TaxAmt], [Project2].[Freight] AS [Freight], [Project2].[TotalDue] AS [TotalDue], [Project2].[Comment] AS [Comment], [Project2].[rowguid] AS [rowguid1], [Project2].[ModifiedDate] AS [ModifiedDate1], [Project2].[C2] AS [C3], [Project2].[BillToAddressID] AS [BillToAddressID], [Project2].[ShipToAddressID] AS [ShipToAddressID], [Project2].[CustomerID] AS [CustomerID1], [Project2].[C1] AS [C4], [Project2].[C1] AS [C5], [Project2].[SalesOrderID1] AS [SalesOrderID1], [Project2].[SalesOrderDetailID] AS [SalesOrderDetailID], [Project2].[OrderQty] AS [OrderQty], [Project2].[UnitPrice] AS [UnitPrice], [Project2].[UnitPriceDiscount] AS [UnitPriceDiscount], [Project2].[LineTotal] AS [LineTotal], [Project2].[rowguid1] AS [rowguid2], [Project2].[ModifiedDate1] AS [ModifiedDate2], [Project2].[C1] AS [C6], [Project2].[ProductID] AS [ProductID], [Project2].[Name] AS [Name], [Project2].[ProductNumber] AS [ProductNumber], [Project2].[Color] AS [Color], [Project2].[StandardCost] AS [StandardCost], [Project2].[ListPrice] AS [ListPrice], [Project2].[Size] AS [Size], [Project2].[Weight] AS [Weight], [Project2].[SellStartDate] AS [SellStartDate], [Project2].[SellEndDate] AS [SellEndDate], [Project2].[DiscontinuedDate] AS [DiscontinuedDate], [Project2].[ThumbNailPhoto] AS [ThumbNailPhoto], [Project2].[ThumbnailPhotoFileName] AS [ThumbnailPhotoFileName], [Project2].[rowguid2] AS [rowguid3], [Project2].[ModifiedDate2] AS [ModifiedDate3], [Project2].[ProductCategoryID] AS [ProductCategoryID], [Project2].[ProductModelID] AS [ProductModelID], CAST(NULL AS int) AS [C7], CAST(NULL AS varchar(1)) AS [C8], CAST(NULL AS uniqueidentifier) AS [C9], CAST(NULL AS datetime2) AS [C10], CAST(NULL AS int) AS [C11], CAST(NULL AS int) AS [C12] FROM [SalesLT].[Customer] AS [Extent1] LEFT OUTER JOIN (SELECT [Extent2].[SalesOrderID] AS [SalesOrderID], [Extent2].[RevisionNumber] AS [RevisionNumber], [Extent2].[OrderDate] AS [OrderDate], [Extent2].[DueDate] AS [DueDate], [Extent2].[ShipDate] AS [ShipDate], [Extent2].[Status] AS [Status], [Extent2].[OnlineOrderFlag] AS [OnlineOrderFlag], [Extent2].[SalesOrderNumber] AS [SalesOrderNumber], [Extent2].[PurchaseOrderNumber] AS [PurchaseOrderNumber], [Extent2].[AccountNumber] AS [AccountNumber], [Extent2].[CustomerID] AS [CustomerID], [Extent2].[ShipToAddressID] AS [ShipToAddressID], [Extent2].[BillToAddressID] AS [BillToAddressID], [Extent2].[ShipMethod] AS [ShipMethod], [Extent2].[CreditCardApprovalCode] AS [CreditCardApprovalCode], [Extent2].[SubTotal] AS [SubTotal], [Extent2].[TaxAmt] AS [TaxAmt], [Extent2].[Freight] AS [Freight], [Extent2].[TotalDue] AS [TotalDue], [Extent2].[Comment] AS [Comment], [Extent2].[rowguid] AS [rowguid], [Extent2].[ModifiedDate] AS [ModifiedDate], [Project1].[SalesOrderID] AS [SalesOrderID1], [Project1].[SalesOrderDetailID] AS [SalesOrderDetailID], [Project1].[OrderQty] AS [OrderQty], [Project1].[UnitPrice] AS [UnitPrice], [Project1].[UnitPriceDiscount] AS [UnitPriceDiscount], [Project1].[LineTotal] AS [LineTotal], [Project1].[rowguid] AS [rowguid1], [Project1].[ModifiedDate] AS [ModifiedDate1], [Project1].[ProductID] AS [ProductID], [Project1].[Name] AS [Name], [Project1].[ProductNumber] AS [ProductNumber], [Project1].[Color] AS [Color], [Project1].[StandardCost] AS [StandardCost], [Project1].[ListPrice] AS [ListPrice], [Project1].[Size] AS [Size], [Project1].[Weight] AS [Weight], [Project1].[ProductCategoryID] AS [ProductCategoryID], [Project1].[ProductModelID] AS [ProductModelID], [Project1].[SellStartDate] AS [SellStartDate], [Project1].[SellEndDate] AS [SellEndDate], [Project1].[DiscontinuedDate] AS [DiscontinuedDate], [Project1].[ThumbNailPhoto] AS [ThumbNailPhoto], [Project1].[ThumbnailPhotoFileName] AS [ThumbnailPhotoFileName], [Project1].[rowguid1] AS [rowguid2], [Project1].[ModifiedDate1] AS [ModifiedDate2], [Project1].[C1] AS [C1], 1 AS [C2] FROM [SalesLT].[SalesOrderHeader] AS [Extent2] LEFT OUTER JOIN (SELECT [Extent3].[SalesOrderID] AS [SalesOrderID], [Extent3].[SalesOrderDetailID] AS [SalesOrderDetailID], [Extent3].[OrderQty] AS [OrderQty], [Extent3].[UnitPrice] AS [UnitPrice], [Extent3].[UnitPriceDiscount] AS [UnitPriceDiscount], [Extent3].[LineTotal] AS [LineTotal], [Extent3].[rowguid] AS [rowguid], [Extent3].[ModifiedDate] AS [ModifiedDate], [Extent4].[ProductID] AS [ProductID], [Extent4].[Name] AS [Name], [Extent4].[ProductNumber] AS [ProductNumber], [Extent4].[Color] AS [Color], [Extent4].[StandardCost] AS [StandardCost], [Extent4].[ListPrice] AS [ListPrice], [Extent4].[Size] AS [Size], [Extent4].[Weight] AS [Weight], [Extent4].[ProductCategoryID] AS [ProductCategoryID], [Extent4].[ProductModelID] AS [ProductModelID], [Extent4].[SellStartDate] AS [SellStartDate], [Extent4].[SellEndDate] AS [SellEndDate], [Extent4].[DiscontinuedDate] AS [DiscontinuedDate], [Extent4].[ThumbNailPhoto] AS [ThumbNailPhoto], [Extent4].[ThumbnailPhotoFileName] AS [ThumbnailPhotoFileName], [Extent4].[rowguid] AS [rowguid1], [Extent4].[ModifiedDate] AS [ModifiedDate1], 1 AS [C1] FROM [SalesLT].[SalesOrderDetail] AS [Extent3] LEFT OUTER JOIN [SalesLT].[Product] AS [Extent4] ON [Extent3].[ProductID] = [Extent4].[ProductID] ) AS [Project1] ON [Extent2].[SalesOrderID] = [Project1].[SalesOrderID] ) AS [Project2] ON [Extent1].[CustomerID] = [Project2].[CustomerID] UNION ALL SELECT 2 AS [C1], [Extent5].[CustomerID] AS [CustomerID], [Extent5].[NameStyle] AS [NameStyle], [Extent5].[Title] AS [Title], [Extent5].[FirstName] AS [FirstName], [Extent5].[MiddleName] AS [MiddleName], [Extent5].[LastName] AS [LastName], [Extent5].[Suffix] AS [Suffix], [Extent5].[CompanyName] AS [CompanyName], [Extent5].[SalesPerson] AS [SalesPerson], [Extent5].[EmailAddress] AS [EmailAddress], [Extent5].[Phone] AS [Phone], [Extent5].[PasswordHash] AS [PasswordHash], [Extent5].[PasswordSalt] AS [PasswordSalt], [Extent5].[rowguid] AS [rowguid], [Extent5].[ModifiedDate] AS [ModifiedDate], 1 AS [C2], CAST(NULL AS int) AS [C3], CAST(NULL AS tinyint) AS [C4], CAST(NULL AS datetime2) AS [C5], CAST(NULL AS datetime2) AS [C6], CAST(NULL AS datetime2) AS [C7], CAST(NULL AS tinyint) AS [C8], CAST(NULL AS bit) AS [C9], CAST(NULL AS varchar(1)) AS [C10], CAST(NULL AS varchar(1)) AS [C11], CAST(NULL AS varchar(1)) AS [C12], CAST(NULL AS varchar(1)) AS [C13], CAST(NULL AS varchar(1)) AS [C14], CAST(NULL AS decimal(19,4)) AS [C15], CAST(NULL AS decimal(19,4)) AS [C16], CAST(NULL AS decimal(19,4)) AS [C17], CAST(NULL AS decimal(19,4)) AS [C18], CAST(NULL AS varchar(1)) AS [C19], CAST(NULL AS uniqueidentifier) AS [C20], CAST(NULL AS datetime2) AS [C21], CAST(NULL AS int) AS [C22], CAST(NULL AS int) AS [C23], CAST(NULL AS int) AS [C24], CAST(NULL AS int) AS [C25], CAST(NULL AS int) AS [C26], CAST(NULL AS int) AS [C27], CAST(NULL AS int) AS [C28], CAST(NULL AS int) AS [C29], CAST(NULL AS smallint) AS [C30], CAST(NULL AS decimal(19,4)) AS [C31], CAST(NULL AS decimal(19,4)) AS [C32], CAST(NULL AS decimal(38,6)) AS [C33], CAST(NULL AS uniqueidentifier) AS [C34], CAST(NULL AS datetime2) AS [C35], CAST(NULL AS int) AS [C36], CAST(NULL AS int) AS [C37], CAST(NULL AS varchar(1)) AS [C38], CAST(NULL AS varchar(1)) AS [C39], CAST(NULL AS varchar(1)) AS [C40], CAST(NULL AS decimal(19,4)) AS [C41], CAST(NULL AS decimal(19,4)) AS [C42], CAST(NULL AS varchar(1)) AS [C43], CAST(NULL AS decimal(8,2)) AS [C44], CAST(NULL AS datetime2) AS [C45], CAST(NULL AS datetime2) AS [C46], CAST(NULL AS datetime2) AS [C47], CAST(NULL AS varbinary(1)) AS [C48], CAST(NULL AS varchar(1)) AS [C49], CAST(NULL AS uniqueidentifier) AS [C50], CAST(NULL AS datetime2) AS [C51], CAST(NULL AS int) AS [C52], CAST(NULL AS int) AS [C53], 1 AS [C54], [Extent6].[AddressType] AS [AddressType], [Extent6].[rowguid] AS [rowguid1], [Extent6].[ModifiedDate] AS [ModifiedDate1], [Extent6].[AddressID] AS [AddressID], [Extent6].[CustomerID] AS [CustomerID1] FROM [SalesLT].[Customer] AS [Extent5] INNER JOIN [SalesLT].[CustomerAddress] AS [Extent6] ON [Extent5].[CustomerID] = [Extent6].[CustomerID]) AS [UnionAll1] ORDER BY [UnionAll1].[CustomerID] ASC, [UnionAll1].[C1] ASC, [UnionAll1].[SalesOrderID] ASC, [UnionAll1].[C4] ASC |
Store Query from Projection SELECT [UnionAll1].[CustomerID] AS [C1], [UnionAll1].[NameStyle] AS [C2], [UnionAll1].[Title] AS [C3], [UnionAll1].[FirstName] AS [C4], [UnionAll1].[MiddleName] AS [C5], [UnionAll1].[LastName] AS [C6], [UnionAll1].[Suffix] AS [C7], [UnionAll1].[CompanyName] AS [C8], [UnionAll1].[SalesPerson] AS [C9], [UnionAll1].[EmailAddress] AS [C10], [UnionAll1].[Phone] AS [C11], [UnionAll1].[PasswordHash] AS [C12], [UnionAll1].[PasswordSalt] AS [C13], [UnionAll1].[rowguid] AS [C14], [UnionAll1].[ModifiedDate] AS [C15], [UnionAll1].[SalesOrderID] AS [C16], [UnionAll1].[SalesOrderDetailID] AS [C17], [UnionAll1].[ProductID] AS [C18], [UnionAll1].[ProductID1] AS [C19], [UnionAll1].[C2] AS [C20], [UnionAll1].[C3] AS [C21], [UnionAll1].[C4] AS [C22], [UnionAll1].[C5] AS [C23], [UnionAll1].[SalesOrderID1] AS [C24], [UnionAll1].[RevisionNumber] AS [C25], [UnionAll1].[OrderDate] AS [C26], [UnionAll1].[DueDate] AS [C27], [UnionAll1].[ShipDate] AS [C28], [UnionAll1].[Status] AS [C29], [UnionAll1].[OnlineOrderFlag] AS [C30], [UnionAll1].[SalesOrderNumber] AS [C31], [UnionAll1].[PurchaseOrderNumber] AS [C32], [UnionAll1].[AccountNumber] AS [C33], [UnionAll1].[ShipMethod] AS [C34], [UnionAll1].[CreditCardApprovalCode] AS [C35], [UnionAll1].[SubTotal] AS [C36], [UnionAll1].[TaxAmt] AS [C37], [UnionAll1].[Freight] AS [C38], [UnionAll1].[TotalDue] AS [C39], [UnionAll1].[Comment] AS [C40], [UnionAll1].[rowguid1] AS [C41], [UnionAll1].[ModifiedDate1] AS [C42], [UnionAll1].[BillToAddressID] AS [C43], [UnionAll1].[ShipToAddressID] AS [C44], [UnionAll1].[CustomerID1] AS [C45], [UnionAll1].[C6] AS [C46], [UnionAll1].[ProductID2] AS [C47], [UnionAll1].[Name] AS [C48], [UnionAll1].[ProductNumber] AS [C49], [UnionAll1].[Color] AS [C50], [UnionAll1].[StandardCost] AS [C51], [UnionAll1].[ListPrice] AS [C52], [UnionAll1].[Size] AS [C53], [UnionAll1].[Weight] AS [C54], [UnionAll1].[SellStartDate] AS [C55], [UnionAll1].[SellEndDate] AS [C56], [UnionAll1].[DiscontinuedDate] AS [C57], [UnionAll1].[ThumbNailPhoto] AS [C58], [UnionAll1].[ThumbnailPhotoFileName] AS [C59], [UnionAll1].[rowguid2] AS [C60], [UnionAll1].[ModifiedDate2] AS [C61], [UnionAll1].[ProductCategoryID] AS [C62], [UnionAll1].[ProductModelID] AS [C63], [UnionAll1].[C1] AS [C64], [UnionAll1].[C7] AS [C65], [UnionAll1].[SalesOrderID2] AS [C66], [UnionAll1].[RevisionNumber1] AS [C67], [UnionAll1].[OrderDate1] AS [C68], [UnionAll1].[DueDate1] AS [C69], [UnionAll1].[ShipDate1] AS [C70], [UnionAll1].[Status1] AS [C71], [UnionAll1].[OnlineOrderFlag1] AS [C72], [UnionAll1].[SalesOrderNumber1] AS [C73], [UnionAll1].[PurchaseOrderNumber1] AS [C74], [UnionAll1].[AccountNumber1] AS [C75], [UnionAll1].[ShipMethod1] AS [C76], [UnionAll1].[CreditCardApprovalCode1] AS [C77], [UnionAll1].[SubTotal1] AS [C78], [UnionAll1].[TaxAmt1] AS [C79], [UnionAll1].[Freight1] AS [C80], [UnionAll1].[TotalDue1] AS [C81], [UnionAll1].[Comment1] AS [C82], [UnionAll1].[rowguid3] AS [C83], [UnionAll1].[ModifiedDate3] AS [C84], [UnionAll1].[BillToAddressID1] AS [C85], [UnionAll1].[ShipToAddressID1] AS [C86], [UnionAll1].[CustomerID2] AS [C87], [UnionAll1].[C8] AS [C88], [UnionAll1].[C17] AS [C89], [UnionAll1].[C9] AS [C90], [UnionAll1].[C10] AS [C91], [UnionAll1].[C11] AS [C92], [UnionAll1].[C12] AS [C93], [UnionAll1].[C13] AS [C94], [UnionAll1].[C14] AS [C95], [UnionAll1].[C15] AS [C96], [UnionAll1].[C16] AS [C97] FROM (SELECT CASE WHEN ([Extent5].[SalesOrderID] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1], [Extent1].[CustomerID] AS [CustomerID], [Extent1].[NameStyle] AS [NameStyle], [Extent1].[Title] AS [Title], [Extent1].[FirstName] AS [FirstName], [Extent1].[MiddleName] AS [MiddleName], [Extent1].[LastName] AS [LastName], [Extent1].[Suffix] AS [Suffix], [Extent1].[CompanyName] AS [CompanyName], [Extent1].[SalesPerson] AS [SalesPerson], [Extent1].[EmailAddress] AS [EmailAddress], [Extent1].[Phone] AS [Phone], [Extent1].[PasswordHash] AS [PasswordHash], [Extent1].[PasswordSalt] AS [PasswordSalt], [Extent1].[rowguid] AS [rowguid], [Extent1].[ModifiedDate] AS [ModifiedDate], [Extent3].[SalesOrderID] AS [SalesOrderID], [Extent3].[SalesOrderDetailID] AS [SalesOrderDetailID], [Extent4].[ProductID] AS [ProductID], [Extent4].[ProductID] AS [ProductID1], 1 AS [C2], 1 AS [C3], 1 AS [C4], 1 AS [C5], [Extent2].[SalesOrderID] AS [SalesOrderID1], [Extent2].[RevisionNumber] AS [RevisionNumber], [Extent2].[OrderDate] AS [OrderDate], [Extent2].[DueDate] AS [DueDate], [Extent2].[ShipDate] AS [ShipDate], [Extent2].[Status] AS [Status], [Extent2].[OnlineOrderFlag] AS [OnlineOrderFlag], [Extent2].[SalesOrderNumber] AS [SalesOrderNumber], [Extent2].[PurchaseOrderNumber] AS [PurchaseOrderNumber], [Extent2].[AccountNumber] AS [AccountNumber], [Extent2].[ShipMethod] AS [ShipMethod], [Extent2].[CreditCardApprovalCode] AS [CreditCardApprovalCode], [Extent2].[SubTotal] AS [SubTotal], [Extent2].[TaxAmt] AS [TaxAmt], [Extent2].[Freight] AS [Freight], [Extent2].[TotalDue] AS [TotalDue], [Extent2].[Comment] AS [Comment], [Extent2].[rowguid] AS [rowguid1], [Extent2].[ModifiedDate] AS [ModifiedDate1], [Extent2].[BillToAddressID] AS [BillToAddressID], [Extent2].[ShipToAddressID] AS [ShipToAddressID], [Extent2].[CustomerID] AS [CustomerID1], 1 AS [C6], [Extent4].[ProductID] AS [ProductID2], [Extent4].[Name] AS [Name], [Extent4].[ProductNumber] AS [ProductNumber], [Extent4].[Color] AS [Color], [Extent4].[StandardCost] AS [StandardCost], [Extent4].[ListPrice] AS [ListPrice], [Extent4].[Size] AS [Size], [Extent4].[Weight] AS [Weight], [Extent4].[SellStartDate] AS [SellStartDate], [Extent4].[SellEndDate] AS [SellEndDate], [Extent4].[DiscontinuedDate] AS [DiscontinuedDate], [Extent4].[ThumbNailPhoto] AS [ThumbNailPhoto], [Extent4].[ThumbnailPhotoFileName] AS [ThumbnailPhotoFileName], [Extent4].[rowguid] AS [rowguid2], [Extent4].[ModifiedDate] AS [ModifiedDate2], [Extent4].[ProductCategoryID] AS [ProductCategoryID], [Extent4].[ProductModelID] AS [ProductModelID], CASE WHEN ([Extent5].[SalesOrderID] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C7], [Extent5].[SalesOrderID] AS [SalesOrderID2], [Extent5].[RevisionNumber] AS [RevisionNumber1], [Extent5].[OrderDate] AS [OrderDate1], [Extent5].[DueDate] AS [DueDate1], [Extent5].[ShipDate] AS [ShipDate1], [Extent5].[Status] AS [Status1], [Extent5].[OnlineOrderFlag] AS [OnlineOrderFlag1], [Extent5].[SalesOrderNumber] AS [SalesOrderNumber1], [Extent5].[PurchaseOrderNumber] AS [PurchaseOrderNumber1], [Extent5].[AccountNumber] AS [AccountNumber1], [Extent5].[ShipMethod] AS [ShipMethod1], [Extent5].[CreditCardApprovalCode] AS [CreditCardApprovalCode1], [Extent5].[SubTotal] AS [SubTotal1], [Extent5].[TaxAmt] AS [TaxAmt1], [Extent5].[Freight] AS [Freight1], [Extent5].[TotalDue] AS [TotalDue1], [Extent5].[Comment] AS [Comment1], [Extent5].[rowguid] AS [rowguid3], [Extent5].[ModifiedDate] AS [ModifiedDate3], [Extent5].[BillToAddressID] AS [BillToAddressID1], [Extent5].[ShipToAddressID] AS [ShipToAddressID1], [Extent5].[CustomerID] AS [CustomerID2], CAST(NULL AS int) AS [C8], CAST(NULL AS int) AS [C9], CAST(NULL AS smallint) AS [C10], CAST(NULL AS decimal(19,4)) AS [C11], CAST(NULL AS decimal(19,4)) AS [C12], CAST(NULL AS decimal(38,6)) AS [C13], CAST(NULL AS uniqueidentifier) AS [C14], CAST(NULL AS datetime2) AS [C15], CAST(NULL AS int) AS [C16], CAST(NULL AS int) AS [C17] FROM [SalesLT].[Customer] AS [Extent1] INNER JOIN [SalesLT].[SalesOrderHeader] AS [Extent2] ON [Extent1].[CustomerID] = [Extent2].[CustomerID] INNER JOIN [SalesLT].[SalesOrderDetail] AS [Extent3] ON [Extent2].[SalesOrderID] = [Extent3].[SalesOrderID] LEFT OUTER JOIN [SalesLT].[Product] AS [Extent4] ON [Extent3].[ProductID] = [Extent4].[ProductID] LEFT OUTER JOIN [SalesLT].[SalesOrderHeader] AS [Extent5] ON [Extent1].[CustomerID] = [Extent5].[CustomerID] UNION ALL SELECT 2 AS [C1], [Extent6].[CustomerID] AS [CustomerID], [Extent6].[NameStyle] AS [NameStyle], [Extent6].[Title] AS [Title], [Extent6].[FirstName] AS [FirstName], [Extent6].[MiddleName] AS [MiddleName], [Extent6].[LastName] AS [LastName], [Extent6].[Suffix] AS [Suffix], [Extent6].[CompanyName] AS [CompanyName], [Extent6].[SalesPerson] AS [SalesPerson], [Extent6].[EmailAddress] AS [EmailAddress], [Extent6].[Phone] AS [Phone], [Extent6].[PasswordHash] AS [PasswordHash], [Extent6].[PasswordSalt] AS [PasswordSalt], [Extent6].[rowguid] AS [rowguid], [Extent6].[ModifiedDate] AS [ModifiedDate], [Extent8].[SalesOrderID] AS [SalesOrderID], [Extent8].[SalesOrderDetailID] AS [SalesOrderDetailID], [Extent9].[ProductID] AS [ProductID], [Extent9].[ProductID] AS [ProductID1], 1 AS [C2], 1 AS [C3], 1 AS [C4], 1 AS [C5], [Extent7].[SalesOrderID] AS [SalesOrderID1], [Extent7].[RevisionNumber] AS [RevisionNumber], [Extent7].[OrderDate] AS [OrderDate], [Extent7].[DueDate] AS [DueDate], [Extent7].[ShipDate] AS [ShipDate], [Extent7].[Status] AS [Status], [Extent7].[OnlineOrderFlag] AS [OnlineOrderFlag], [Extent7].[SalesOrderNumber] AS [SalesOrderNumber], [Extent7].[PurchaseOrderNumber] AS [PurchaseOrderNumber], [Extent7].[AccountNumber] AS [AccountNumber], [Extent7].[ShipMethod] AS [ShipMethod], [Extent7].[CreditCardApprovalCode] AS [CreditCardApprovalCode], [Extent7].[SubTotal] AS [SubTotal], [Extent7].[TaxAmt] AS [TaxAmt], [Extent7].[Freight] AS [Freight], [Extent7].[TotalDue] AS [TotalDue], [Extent7].[Comment] AS [Comment], [Extent7].[rowguid] AS [rowguid1], [Extent7].[ModifiedDate] AS [ModifiedDate1], [Extent7].[BillToAddressID] AS [BillToAddressID], [Extent7].[ShipToAddressID] AS [ShipToAddressID], [Extent7].[CustomerID] AS [CustomerID1], 1 AS [C6], [Extent9].[ProductID] AS [ProductID2], [Extent9].[Name] AS [Name], [Extent9].[ProductNumber] AS [ProductNumber], [Extent9].[Color] AS [Color], [Extent9].[StandardCost] AS [StandardCost], [Extent9].[ListPrice] AS [ListPrice], [Extent9].[Size] AS [Size], [Extent9].[Weight] AS [Weight], [Extent9].[SellStartDate] AS [SellStartDate], [Extent9].[SellEndDate] AS [SellEndDate], [Extent9].[DiscontinuedDate] AS [DiscontinuedDate], [Extent9].[ThumbNailPhoto] AS [ThumbNailPhoto], [Extent9].[ThumbnailPhotoFileName] AS [ThumbnailPhotoFileName], [Extent9].[rowguid] AS [rowguid2], [Extent9].[ModifiedDate] AS [ModifiedDate2], [Extent9].[ProductCategoryID] AS [ProductCategoryID], [Extent9].[ProductModelID] AS [ProductModelID], CAST(NULL AS int) AS [C7], CAST(NULL AS int) AS [C8], CAST(NULL AS tinyint) AS [C9], CAST(NULL AS datetime2) AS [C10], CAST(NULL AS datetime2) AS [C11], CAST(NULL AS datetime2) AS [C12], CAST(NULL AS tinyint) AS [C13], CAST(NULL AS bit) AS [C14], CAST(NULL AS varchar(1)) AS [C15], CAST(NULL AS varchar(1)) AS [C16], CAST(NULL AS varchar(1)) AS [C17], CAST(NULL AS varchar(1)) AS [C18], CAST(NULL AS varchar(1)) AS [C19], CAST(NULL AS decimal(19,4)) AS [C20], CAST(NULL AS decimal(19,4)) AS [C21], CAST(NULL AS decimal(19,4)) AS [C22], CAST(NULL AS decimal(19,4)) AS [C23], CAST(NULL AS varchar(1)) AS [C24], CAST(NULL AS uniqueidentifier) AS [C25], CAST(NULL AS datetime2) AS [C26], CAST(NULL AS int) AS [C27], CAST(NULL AS int) AS [C28], CAST(NULL AS int) AS [C29], 1 AS [C30], [Extent10].[SalesOrderDetailID] AS [SalesOrderDetailID1], [Extent10].[OrderQty] AS [OrderQty], [Extent10].[UnitPrice] AS [UnitPrice], [Extent10].[UnitPriceDiscount] AS [UnitPriceDiscount], [Extent10].[LineTotal] AS [LineTotal], [Extent10].[rowguid] AS [rowguid3], [Extent10].[ModifiedDate] AS [ModifiedDate3], [Extent10].[ProductID] AS [ProductID3], [Extent10].[SalesOrderID] AS [SalesOrderID2] FROM [SalesLT].[Customer] AS [Extent6] INNER JOIN [SalesLT].[SalesOrderHeader] AS [Extent7] ON [Extent6].[CustomerID] = [Extent7].[CustomerID] INNER JOIN [SalesLT].[SalesOrderDetail] AS [Extent8] ON [Extent7].[SalesOrderID] = [Extent8].[SalesOrderID] LEFT OUTER JOIN [SalesLT].[Product] AS [Extent9] ON [Extent8].[ProductID] = [Extent9].[ProductID] INNER JOIN [SalesLT].[SalesOrderDetail] AS [Extent10] ON [Extent7].[SalesOrderID] = [Extent10].[SalesOrderID]) AS [UnionAll1] ORDER BY [UnionAll1].[CustomerID] ASC, [UnionAll1].[SalesOrderID] ASC, [UnionAll1].[SalesOrderDetailID] ASC, [UnionAll1].[ProductID] ASC, [UnionAll1].[ProductID1] ASC, [UnionAll1].[SalesOrderID1] ASC, [UnionAll1].[ProductID2] ASC, [UnionAll1].[C1] ASC |
What about the time that it takes to process these queries. I don't mean execute them, but compile them into store queries.
I tested this by calling ToTraceString to get the query compiled. The query using Include is an ObjectQuery, so not casting was involved. The projection query is a LINQ to Entities query and therefore I cast it to an ObjectQuery in order to use the ToTrace String method. I tested these independently a number of times, each time in a new application instance. The ObjectQuery.Include query was slightly faster. Looking at the functions that were executed, it looks like the LINQ expression was parsed before ToTraceSTring was called, so that didn't add to the ToTraceString time.
I'll also compare these to 4 tests of calling ToTraceString on *only* getting customers (context.Customers), with no eager loading of related data.
| test | Include | Projection | No Eager Loading |
| 1 | 1090.26 ms | 1176.54 | 510.51 |
| 2 | 909.89 ms | 1155.44 | 518.89 |
| 3 | 899.92ms | 1164.55 | 515.28 |
| 4 | 917.48ms | 1294.00 | 520.88 |
So the include is a tad faster than the projection and easier to code, too. And eager loading takes about twice as long as only getting the base objects. When you call subsequent loads for the related data, there's much less work to do because only single entities are being returned, not complex, shaped data.
Don't ignore the fact that when you are eager loading, you are also bring back a *lot* more data. And all of that data needs to get materialized.
So we're back to balance.
Don't forget about pre-compiling queries
Eager loading is more expensive, not only because of the additional data, but because of the extra effort in compiling the queries. But don't forget about pre-compiling LINQ queries, pre-compiling views and the benefits of Entity SQL query caching. All of these tools can be leveraged to *greatly* reduce the expense of compiling the queries.
Then you can weigh these costs against returning to the database over and over again to get related data. Having an understanding of what the costs are so that you can make those decisions is essential in applications where performance is critical.
I was inspired to look more deeply into this because of a recommendation made by Jarek Kowalksi (from the EF team) to a poster on the MSDN EF Forums who was concerned about query performance. You can read that thread here.




