Skip to content

Reuse a single-result subquery across its projected members#38502

Merged
AndriySvyryd merged 2 commits into
dotnet:mainfrom
wassim-k:fix/7776_duplicate-subquery
Jun 30, 2026
Merged

Reuse a single-result subquery across its projected members#38502
AndriySvyryd merged 2 commits into
dotnet:mainfrom
wassim-k:fix/7776_duplicate-subquery

Conversation

@wassim-k

@wassim-k wassim-k commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

I've put together a small interim fix for #7776 and wanted to share it for your consideration. I'd completely understand if you'd rather wait for the pending-selector rework in #20291 (which you've noted #7776 is probably a duplicate of). But since that's a larger effort, a self-contained stopgap might be useful in the meantime.

I've kept it intentionally scoped to a single ~100-line file, plus a one-line call in NavigationExpandingExpressionVisitor. Easy to replace in the future when the root fix lands.

It fires only when a projection reads two or more members of one correlated single-result subquery, and rewrites that into a SelectMany so the members come from one join. Everything else is left untouched. It doesn't address the general reused-subquery case (#20291 would), only this shape.

Tested across the single-result operators (First/Single/Last/ElementAt and their OrDefault/predicate forms) on SQL Server, SQLite and the in-memory provider; the full SQL Server suite passes with no baseline changes.

…otnet#7776)

A projection that reads two or more members of one correlated First/Single/
Last/ElementAt subquery now lifts to a single SelectMany during navigation
expansion, so the members come from a single join instead of a duplicated
subquery per member.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a targeted navigation-expansion rewrite to avoid duplicating correlated single-result subqueries when multiple members are projected from the same subquery (e.g. c.City, c.Country, c.ContactName), by lifting the subquery into a single join/SelectMany shape so all projected members come from the same joined row.

Changes:

  • Add LiftSingleResultSubqueries to rewrite repeated member-access projections over the same correlated single-result subquery into a single SelectMany.
  • Invoke the lifting pass after ProcessSelect in NavigationExpandingExpressionVisitor for Queryable.Select.
  • Add a cross-provider spec test plus SQL Server SQL baseline coverage; Cosmos asserts translation failure.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.SingleResultSubqueryLifting.cs New lifting pass to detect repeated member accesses over correlated single-result subqueries and rewrite to a single join via SelectMany.
src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs Hooks the lifting pass into the Queryable.Select handling path.
test/EFCore.Specification.Tests/Query/NorthwindSelectQueryTestBase.cs Adds the new scenario as a provider-agnostic spec test across single-result operators.
test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSelectQuerySqlServerTest.cs Adds SQL Server baseline assertions verifying the lifted single-join translation shape.
test/EFCore.Cosmos.FunctionalTests/Query/NorthwindSelectQueryCosmosTest.cs Marks the new test as translation-failing for Cosmos.

@AndriySvyryd AndriySvyryd self-assigned this Jun 29, 2026

@AndriySvyryd AndriySvyryd left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your contribution!

@AndriySvyryd AndriySvyryd merged commit 347acfc into dotnet:main Jun 30, 2026
15 checks passed
@wassim-k wassim-k deleted the fix/7776_duplicate-subquery branch June 30, 2026 01:53
@dotnet-milestone-bot dotnet-milestone-bot Bot added this to the 11.0-preview7 milestone Jul 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants