Skip to content

Conversation

roji
Copy link
Member

@roji roji commented Jul 27, 2025

With JSON owned entities, the root (non-JSON) entity is materialized and tracked, and then JSON shaper code materializes the JSON owned entity and tracks that separately; the change tracker does the fix-up etc. When originally implementing the shaper support for complex JSON, I also added JSON shaper code after materialization - similar to the owned flow - but that's too late, since StartTracking already gets called as part of materiailzation.

I considered separating StartTracking from materialization and pulling the former up, so that I can inject JSON shaper code in between - but that's quite a significant change would probably break external (non-relational) providers. Instead I opted for adding an extension hook during materialization, that gets called after the structural type is instantiated and its regular properties are populated, but before it's handed off to StartTracking().

Adding such a hook proved difficult - our shaper generation really isn't designed with extensibility in mind. The most problematic is that in relational, ShapedQueryCompilingExpressionVisitor immediately calls a separate private visitor - ShaperProcessingExpressionVisitor - which does most of the work, and occasionally calls into the "parent" ShaperProcessingExpressionVisitor. One of these calls is for structural type materialization, making it difficult to have the extension hook call back into ShaperProcessingExpressionVisitor, which is where the JSON shaper logic lives (and it relies on various global state inside that visitor, and so can't easily be moved out). So I ended up tracking the current ShaperProcessingExpressionVisitor on RelationalShapedQueryCompilingExpressionVisitor, so that the hook can call into it; and since ShaperProcessingExpressionVisitor instantiates and calls itself recursively, that also needs to be accounted for.

There's a lot of potential for simplifying and cleaning everything up here, I hope we get to it at some point.

/cc @artl93

Visit(relationalGroupByResultExpression.KeyIdentifier),
QueryCompilationContext.QueryContextParameter,
_dataReaderParameter);
try
Copy link
Member Author

Choose a reason for hiding this comment

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

Just indented the code to add the management of _parentVisitor._currentShaperProcessor.


relationalCommandResolver = _parentVisitor.CreateRelationalCommandResolverExpression(_selectExpression);
readerColumns = _readerColumns;
relatedDataLoaders = null;
Copy link
Member Author

Choose a reason for hiding this comment

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

As above, only indentation change.

@roji roji force-pushed the ComplexJsonChangeTracking branch from 73d2e74 to 4b9bc54 Compare July 27, 2025 10:31
Name: 'Test Company'
Contacts (Complex: List<Contact>)
Department (Complex: Department)
Budget: 10000.00
Copy link
Member Author

Choose a reason for hiding this comment

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

This failed on SQLite because we get Budget: 10000.0.

Copy link
Member

Choose a reason for hiding this comment

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

Add something like Assert.DoesNotContain("Exception");

@roji roji marked this pull request as ready for review July 27, 2025 11:21
@roji roji requested a review from a team as a code owner July 27, 2025 11:21
@roji roji mentioned this pull request Jul 25, 2025
15 tasks
@roji roji force-pushed the ComplexJsonChangeTracking branch from 4b9bc54 to 6cf70ef Compare July 28, 2025 22:37
@roji roji enabled auto-merge (squash) July 28, 2025 22:38
@roji roji merged commit 0cb0563 into dotnet:main Jul 28, 2025
7 checks passed
@roji roji deleted the ComplexJsonChangeTracking branch July 28, 2025 23:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants