I was trying Include extension method from http://damieng.com/blog/2010/05/21/include-for-linq-to-sql-and-maybe-other-providers, but it does not really work.
So I have this query in Linq-to-SQL:
var items = dataContext.Items()
.Where(x => x.Id < 100)
.ToList();
and data context has load option LoadWith(x => x.ItemImages).
That produces this SQL statement:
SELECT [t0].[Id], [t0].[Number], [t0].[Title], [t1].[Id] AS [Id2], [t1].[ItemId], [t1].[Url], (
SELECT COUNT(*)
FROM [dbo].[ItemImage] AS [t2]
WHERE [t2].[ItemId] = [t0].[Id]
) AS [value]
FROM [dbo].[Item] AS [t0]
LEFT OUTER JOIN [dbo].[ItemImage] AS [t1] ON [t1].[ItemId] = [t0].[Id]
WHERE [t0].[Id] < @p0
ORDER BY [t0].[Id], [t1].[Id]
Now, if I don’t use data load options, but rewrite query to:
var items = dataContext.Items()
.Where(x => x.Id < 100)
.Select(x=>new Tuple<Item, EntitySet<ItemImage>>(x, x.ItemImages))
.AsEnumerable()
.Select(x=>x.Item1)
.ToList();
The resulting SQL statement is the same:
SELECT [t0].[Id], [t0].[Number], [t0].[Title], [t1].[Id] AS [Id2], [t1].[ItemId], [t1].[Url], (
SELECT COUNT(*)
FROM [dbo].[ItemImage] AS [t2]
WHERE [t2].[ItemId] = [t0].[Id]
) AS [value]
FROM [dbo].[Item] AS [t0]
LEFT OUTER JOIN [dbo].[ItemImage] AS [t1] ON [t1].[ItemId] = [t0].[Id]
WHERE [t0].[Id] < @p0
ORDER BY [t0].[Id], [t1].[Id]
But, if I’m accessing items[0].ItemImages property, it does request to SQL Server to retrieve item images, so it looks like it had all required data to avoid additional queries, but materialization went wrong and it still does additional queries, although it could avoid them.
May I somehow fix this?
UPDATE: I was quite skeptical about performance, using LoadWith options, and thought that doing two queries (one for items, second for images) and mapping in code would be faster, than Linq2Sql generated single SQL query, but at least with my data amounts single query works faster, so it would be quite interesting to see a solution.
The include method on my blog was only demonstrated and tested with a to-one relationship.
By projecting a query that references an to-one it populates the LINQ to SQL identity cache. When any navigation property is later navigated it will hit that cache first.
Unfortunately LINQ to SQL isn’t advanced enough to cache to-many associations in this way.
Your options are either to use LoadWith or write a query that projects and groups by, e.g.
The caveat to this query would be that you wouldn’t see any items that don’t have ItemImages.