Skip to content

Improve the query generated for many-to-many explicit/lazy load #22022

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Open
Tracked by #22960
ajcvickers opened this issue Aug 11, 2020 · 3 comments
Open
Tracked by #22960

Improve the query generated for many-to-many explicit/lazy load #22022

ajcvickers opened this issue Aug 11, 2020 · 3 comments

Comments

@ajcvickers
Copy link
Contributor

ajcvickers commented Aug 11, 2020

We made a patch fix to the query in #23589. This issue is now in 6.0 to add a cleaner, non-patch solution.


Original issue

The query must:

  • Return the loaded entities
  • Track the loaded entities
  • Track the join entities associated with the loaded entities

This is what we currently generate:

DbSet<EntityOne>()
    .AsTracking()
    .Where(e => EF.Property<int>(e, "Id") == (int)ValueBuffer.get_Item(0))
    .SelectMany(e => e.TwoSkip)
    .Include(e => e.OneSkip
        .Where(e => EF.Property<int>(e, "Id") == (int)ValueBuffer.get_Item(0)))

Which translates to the following on SQL Server:

DECLARE @__p_0 int = 3;

SELECT [t].[Id], [t].[CollectionInverseId], [t].[Name], [t].[ReferenceInverseId], [e].[Id], [t].[OneId], [t].[TwoId], [t0].[OneId], [t0].[TwoId], [t0].[Id], [t0].[Name]
FROM [EntityOnes] AS [e]
INNER JOIN (
    SELECT [e0].[Id], [e0].[CollectionInverseId], [e0].[Name], [e0].[ReferenceInverseId], [j].[OneId], [j].[TwoId]
    FROM [JoinOneToTwo] AS [j]
    INNER JOIN [EntityTwos] AS [e0] ON [j].[TwoId] = [e0].[Id]
) AS [t] ON [e].[Id] = [t].[OneId]
LEFT JOIN (
    SELECT [j0].[OneId], [j0].[TwoId], [e1].[Id], [e1].[Name]
    FROM [JoinOneToTwo] AS [j0]
    INNER JOIN [EntityOnes] AS [e1] ON [j0].[OneId] = [e1].[Id]
    WHERE [e1].[Id] = @__p_0
) AS [t0] ON [t].[Id] = [t0].[TwoId]
WHERE [e].[Id] = @__p_0
ORDER BY [e].[Id], [t].[OneId], [t].[TwoId], [t].[Id], [t0].[OneId], [t0].[TwoId], [t0].[Id]
@smitpatel
Copy link
Contributor

How about

DbSet<EntityOne>()
    .AsTracking()
    .Where(e => EF.Property<int>(e, "Id") == (int)ValueBuffer.get_Item(0))
    .Include(e => e.TwoSkip)
    .ToList()
    .SelectMany(e => e.TwoSkip)

Include will bring join entity. EntityOne should not materialize again if already tracked by context. And client side SelectMany will correct the returning shape.

@smitpatel
Copy link
Contributor

Client side SelectMany is not possible since it returns IQueryable.

@ajcvickers
Copy link
Contributor Author

Removing from milestone to use this to track better fix for #23475.

# for free to join this conversation on GitHub. Already have an account? # to comment
Projects
None yet
Development

No branches or pull requests

4 participants