Repository navigation
Don't reject JSON entities reached only through Include under AsNoTrackingWithIdentityResolution - #38556
Merged
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
Fixes a regression where .Include() over a relational collection whose element has JSON-mapped owned data (.ToJson()) incorrectly threw under .AsNoTrackingWithIdentityResolution(). The PR narrows the JSON entity ordering validator so it only rejects truly unsafe JSON projections (i.e., those produced by queryable operators), while treating Include-only JSON paths as safe, and adjusts Include traversal to avoid re-validating subtrees with the wrong “inside include” state.
Changes:
- Exempt JSON projections reached only via Include from
NoTrackingWithIdentityResolutionrejection, while keeping explicit queryable JSON operations rejected. - Fix
IncludeExpressionvalidation traversal to avoid revisitingNavigationExpressionwith_insideIncludereset. - Add regression tests for the Include + JSON +
AsNoTrackingWithIdentityResolutionscenario, including a combined Include + separate JSON projection case and Cosmos behavior documentation.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.cs | Adjusts JSON ordering validator guards for Include-only paths and fixes Include traversal to preserve correct _insideInclude state. |
| test/EFCore.Specification.Tests/Query/JsonQueryTestBase.cs | Adds cross-provider regression tests covering Include + JSON under AsNoTrackingWithIdentityResolution, including a combined Include + separate projection scenario. |
| test/EFCore.SqlServer.FunctionalTests/Query/JsonQuerySqlServerTest.cs | Adds SQL assertion coverage for the new Include + JSON + AsNoTrackingWithIdentityResolution test on SQL Server. |
| test/EFCore.SqlServer.FunctionalTests/Query/JsonQueryJsonTypeSqlServerTest.cs | Adds the same SQL assertion coverage for the JSON-type SQL Server variant. |
| test/EFCore.Cosmos.FunctionalTests/Query/JsonQueryCosmosTest.cs | Overrides new tests to assert Cosmos’ pre-existing NonEmbeddedIncludeNotSupported behavior remains unchanged. |
…NoTrackingWithIdentityResolution Fixes dotnet#35468. Include on a collection navigation whose target has JSON-mapped (.ToJson()) owned data threw InvalidOperationException under AsNoTrackingWithIdentityResolution, because Include can't cause the JSON-ordering corruption the check guards against. Narrowed the guard to exempt Include-only paths, fixed a fallout bug where doing so skipped revalidating part of the shaper tree, and added regression tests. <details> <summary>Full explanation</summary> Combining `.Include()` on a relational collection navigation with `.AsNoTrackingWithIdentityResolution()` incorrectly threw: InvalidOperationException: Projecting queryable operations on JSON collection is not supported for 'NoTrackingWithIdentityResolution'. whenever the included entity carried JSON-mapped (`.ToJson()`) owned data. This was a regression from EF Core 8 to 9/10. `JsonCorrectOrderOfEntitiesForChangeTrackerValidator` throws to prevent JSON entities from landing in the change tracker out of order when a JSON collection is reordered/filtered by an arbitrary LINQ operator. But `Include` cannot filter or reorder owned/JSON navigations, so JSON entities reached purely through `Include` can never trigger that corruption and are always safe. The validator's guard didn't account for this: `case JsonProjectionInfo when _insideCollection` threw unconditionally as soon as a JSON entity was nested inside any collection shaper, even when it was also inside an `Include`. The fix narrows both throwing guards to `JsonProjectionInfo when _insideCollection && !_insideInclude`, so Include-reached JSON entities/collections fall through to the existing `_includedJsonEntityTypes` tracking instead of throwing. `QueryableJsonProjectionInfo` (an explicit LINQ operator applied directly to a JSON collection) still throws unconditionally in all cases, since that shape cannot occur through `Include` and remains genuinely unsafe. Suppressing the throw exposed a second issue: `IncludeExpression` handling used to fall through to `base.VisitExtension`, which visits both `EntityExpression` and `NavigationExpression` - redundantly revisiting `NavigationExpression` a second time with `_insideInclude` reset to false, which would re-trigger the very exception being fixed. The fix now visits `NavigationExpression` once (with `_insideInclude` true) and explicitly visits `EntityExpression` before returning, so every JSON-bearing node in the shaper tree - including chained `.Include(a).Include(b)` chains - is visited exactly once with the correct `_insideInclude` state. Test coverage added: - `Entity_including_collection_with_json_AsNoTrackingWithIdentityResolution`: reproduces the reported bug shape (Include on a relational collection navigation whose target carries JSON data). - `Entity_including_collection_with_json_and_separate_json_projection_AsNoTrackingWithIdentityResolution`: combines Include of a JSON-bearing collection with a separate, non-Include projection of the same JSON entity type in the same query, exercising the `_includedJsonEntityTypes` / `_projectedKeyAccessInfos` dedup path in `Validate()`. - Cosmos overrides documenting that Cosmos already rejects this Include for an unrelated, pre-existing reason (`CosmosStrings.NonEmbeddedIncludeNotSupported`), confirming this fix doesn't touch Cosmos behavior. Verified no regression: all ~80 existing `*_AsNoTrackingWithIdentityResolution` tests that don't use Include continue to throw as before (the genuinely-unsafe cases this validator protects), the full `JsonQuerySqlServerTest` and `JsonQueryCosmosTest` suites are green, and the exact repro from the issue (SQLite, `OwnsOne(...).ToJson()` on a collection element) no longer throws. </details>
ajcvickers
force-pushed
the
fix-json-ntwir-include-35468
branch
from
July 5, 2026 10:41
45acead to
ee8a8ef
Compare
AndriySvyryd
approved these changes
Jul 7, 2026
Member
|
Thanks for your contribution! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #35468.
Include on a collection navigation whose target has JSON-mapped (.ToJson()) owned data threw InvalidOperationException under AsNoTrackingWithIdentityResolution, because Include can't cause the JSON-ordering corruption the check guards against. Narrowed the guard to exempt Include-only paths, fixed a fallout bug where doing so skipped revalidating part of the shaper tree, and added regression tests.
Full explanation
Combining
.Include()on a relational collection navigation with.AsNoTrackingWithIdentityResolution()incorrectly threw:whenever the included entity carried JSON-mapped (
.ToJson()) owned data. This was a regression from EF Core 8 to 9/10.JsonCorrectOrderOfEntitiesForChangeTrackerValidatorthrows to prevent JSON entities from landing in the change tracker out of order when a JSON collection is reordered/filtered by an arbitrary LINQ operator. ButIncludecannot filter or reorder owned/JSON navigations, so JSON entities reached purely throughIncludecan never trigger that corruption and are always safe. The validator's guard didn't account for this:case JsonProjectionInfo when _insideCollectionthrew unconditionally as soon as a JSON entity was nested inside any collection shaper, even when it was also inside anInclude.The fix narrows both throwing guards to
JsonProjectionInfo when _insideCollection && !_insideInclude, so Include-reached JSON entities/collections fall through to the existing_includedJsonEntityTypestracking instead of throwing.QueryableJsonProjectionInfo(an explicit LINQ operator applied directly to a JSON collection) still throws unconditionally in all cases, since that shape cannot occur throughIncludeand remains genuinely unsafe.Suppressing the throw exposed a second issue:
IncludeExpressionhandling used to fall through tobase.VisitExtension, which visits bothEntityExpressionandNavigationExpression- redundantly revisitingNavigationExpressiona second time with_insideIncludereset to false, which would re-trigger the very exception being fixed. The fix now visitsNavigationExpressiononce (with_insideIncludetrue) and explicitly visitsEntityExpressionbefore returning, so every JSON-bearing node in the shaper tree - including chained.Include(a).Include(b)chains - is visited exactly once with the correct_insideIncludestate.Test coverage added:
Entity_including_collection_with_json_AsNoTrackingWithIdentityResolution: reproduces the reported bug shape (Include on a relational collection navigation whose target carries JSON data).Entity_including_collection_with_json_and_separate_json_projection_AsNoTrackingWithIdentityResolution: combines Include of a JSON-bearing collection with a separate, non-Include projection of the same JSON entity type in the same query, exercising the_includedJsonEntityTypes/_projectedKeyAccessInfosdedup path inValidate().CosmosStrings.NonEmbeddedIncludeNotSupported), confirming this fix doesn't touch Cosmos behavior.Verified no regression: all ~80 existing
*_AsNoTrackingWithIdentityResolutiontests that don't use Include continue to throw as before (the genuinely-unsafe cases this validator protects), the fullJsonQuerySqlServerTestandJsonQueryCosmosTestsuites are green, and the exact repro from the issue (SQLite,OwnsOne(...).ToJson()on a collection element) no longer throws.