Skip to content

Fix: Cosmos ToList on structural collection is treated as scalar#38122

Open
JoasE wants to merge 1 commit intodotnet:mainfrom
JoasE:fix/#38121
Open

Fix: Cosmos ToList on structural collection is treated as scalar#38122
JoasE wants to merge 1 commit intodotnet:mainfrom
JoasE:fix/#38121

Conversation

@JoasE
Copy link
Copy Markdown
Contributor

@JoasE JoasE commented Apr 16, 2026

Don't bind non scalar subquery results for ToList projections
This would change behaviour from 10.0.0, where some queries compiled and might have worked, but actually didn't use a real materializer. If the user didn't have any custom ef config and their model matches the json
Closes: #38121

… projection

Don't bind non scalar subquery results for ToList projections
@JoasE JoasE changed the title Fix: ToList on structural collection property is treaded as scalar Cosmos fix: ToList on structural collection property is treaded as scalar Apr 16, 2026
Copy link
Copy Markdown
Contributor Author

@JoasE JoasE left a comment

Choose a reason for hiding this comment

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

I think these changes should be temporary, and #34067 will revert them.
I'm not as familiar with the projection binding code as other area's, but FWIG, this is the best solution right now
A bit of extra explanation per test case:

"""
SELECT VALUE o["Details"]
SELECT VALUE o
FROM root c
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This projection now happens on the client side in the materializer. This used to generate a JsonConvert.Deserialize call for materialization

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Because this is a SelectMany, we do bind the c["Orders"], but the "Details" projection part happens in the materializer

public override Task Json_collection_leaf_filter_in_projection(bool async)
=> Fixture.NoSyncTest(
async, async a =>
{
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This used to generate a JsonConvert.Deserialize call for materialization. I think we can't translate this because the Where would have to be ran in the materializer since we don't bind ["OwnedReferenceRoot"]["OwnedReferenceBranch"]["OwnedCollectionLeaf"], and 34067 should solve that

public override Task Distinct()
=> AssertTranslationFailed(base.Distinct);

public override async Task Distinct_projected(QueryTrackingBehavior queryTrackingBehavior)
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This used to generate a JsonConvert.Deserialize call for materialization. I think we can't translate this because the Distinct would have to be ran in the materializer since we don't bind ["AssociateCollection"], and 34067 should solve that

@JoasE JoasE changed the title Cosmos fix: ToList on structural collection property is treaded as scalar Fix: Cosmos ToList on structural collection is treaded as scalar Apr 16, 2026
@JoasE JoasE changed the title Fix: Cosmos ToList on structural collection is treaded as scalar Fix: Cosmos ToList on structural collection is treated as scalar Apr 16, 2026
@JoasE JoasE marked this pull request as ready for review April 16, 2026 14:21
@JoasE JoasE requested a review from a team as a code owner April 16, 2026 14:21
Copilot AI review requested due to automatic review settings April 16, 2026 14:21
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes Cosmos query projection binding for ToList() over structural (non-scalar) collections by avoiding scalar projection binding for non-scalar subquery results.

Changes:

  • Adjust Cosmos projection binding so ToList() only binds scalar array subquery results as a scalar projection; non-scalar results are visited/shaped instead.
  • Update Cosmos functional test SQL baselines for owned structural collection ToList() projections.
  • Update/realign Cosmos tests to assert translation failure for scenarios that were previously (incorrectly) translated for structural collections (e.g. Where/Distinct composed before ToList()).

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
test/EFCore.Cosmos.FunctionalTests/Query/OwnedQueryCosmosTest.cs Updates expected Cosmos SQL to project the owning object (o) rather than o["Details"] for structural collection ToList() projections.
test/EFCore.Cosmos.FunctionalTests/Query/JsonQueryCosmosTest.cs Changes a JSON structural collection filter-in-projection test to assert translation failure instead of asserting generated SQL.
test/EFCore.Cosmos.FunctionalTests/Query/Associations/ComplexProperties/ComplexPropertiesCollectionCosmosTest.cs Changes Distinct_projected to assert translation failure rather than asserting generated SQL.
src/EFCore.Cosmos/Query/Internal/CosmosProjectionBindingExpressionVisitor.cs Prevents binding non-scalar array subquery results (e.g. structural/object arrays) as scalar projections when translating ToList(); adds a translation-failure throw for unexpected Queryable methods in this client-eval path.

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.

Cosmos: ToList on structural collection property is treated as scalar in projection

3 participants