fix(strawberryshake): preserve entity fields across differing selection sets (#8063)#11
Open
cliedeman wants to merge 1 commit into
Open
fix(strawberryshake): preserve entity fields across differing selection sets (#8063)#11cliedeman wants to merge 1 commit into
cliedeman wants to merge 1 commit into
Conversation
…on sets Strawberry Shake lost data after deserialization when an entity was referenced more than once in a query with different selection sets, including the inline-fragment ordering case in ChilliCream#8063. The JSON result builder filled the entity store recursively: a nested reference wrote the entity first, then the outer reference overwrote it, replacing the fields it had not selected with defaults. The result factory then read those defaulted fields (or hit a non-null constructor) and surfaced the data as missing or as Value cannot be null. The fix changes the generated entity deserializer so that, when an entity is about to be created, the constructor arguments are evaluated first (which is what triggers any recursive write of the same entity), and the store is then re-checked. If the entity now exists, the already-deserialized fields are preserved instead of being overwritten with defaults. JsonResultBuilderGenerator_UpdateEntity now hoists each response-derived argument into a local and, in the new-entity branch, re-runs TryGetEntity and merges. MethodCallBuilder.AddOutArgument gains a null type reference to emit `out entity` when reusing the existing out variable. Adds two runtime tests driven through the in-memory transport: - RecursiveEntitySelfReference reproduces the loss (red before this change: bestFriend.Age came back 0 instead of 42) and now passes. - InlineFragmentOrder covers the exact ChilliCream#8063 shape (an interface field selected twice with different inline-fragment coverage). All affected generator snapshots and integration client outputs were regenerated. Fixes ChilliCream#8063
10 tasks
cliedeman
force-pushed
the
fix/ss-inline-fragment-order-missing-data
branch
from
June 27, 2026 14:34
64ee963 to
dbc6736
Compare
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.
Summary
Fixes a Strawberry Shake runtime bug where data goes missing after deserialization when an entity is referenced more than once in a query with different selection sets. This is the root cause behind the inline-fragment-ordering report in ChilliCream#8063.
Root cause
When an interface/object field carries an
id, Strawberry Shake treats it as an entity and fills the entity store recursively. The generated JSON result builder constructed an entity by inlining the constructor arguments directly, and the new-entity branch useddefault!for every field not present in the current selection set.If the same entity id was written first by a nested/sibling reference (with one selection set) and then again by an outer reference (with a different, partial selection set), the second
SetEntitycall overwrote the first write, replacing the not-selected fields withdefault!. The result factory then read those defaulted fields, surfacing the data as missing /null, or throwingArgumentNullException/Value cannot be null.for non-nullable fields.The minimal reproduction (the self-referential
selfishGuy.bestFriend == selfishGuycase): the outerselfishGuyselection (id/firstName/lastName) overwrote theage/phonethat the innerbestFriendselection had just written, sobestFriend.Agecame back0.Fix
JsonResultBuilderGenerator_UpdateEntitynow, when constructing an entity:TryGetEntity. If the entity now exists, the already-deserialized fields are preserved (merged) instead of being overwritten with defaults.MethodCallBuilder.AddOutArgumentaccepts a nullable type reference so it can emitout entity(reusing the existing out variable) for the re-check.Generated
elsebranch, before vs after:This mirrors the approach in upstream draft PR ChilliCream#8595, scoped down to the single generator change plus the regenerated outputs.
Tests (red → green)
Two runtime tests driven through the in-memory transport (real client, real result builder, real entity store):
RecursiveEntitySelfReferenceTestreproduces the loss. Before the fix it failed withExpected: 42 / Actual: 0forbestFriend.Age; it now passes.InlineFragmentOrderTestcovers the exact Order of Inline Fragments Causes Missing Data ChilliCream/graphql-platform#8063 shape: an interface field (pet: IPet) selected under two top-level fields where the first omits one implementing type's inline fragment and the second includes it. It asserts the second field'sBird.WingSpansurvives.Files changed
Generators/JsonResultBuilderGenerator_UpdateEntity.cs– the fix (evaluate args, re-check store, merge).Builders/MethodCallBuilder.cs– nullableouttype reference.*.Client.cs+ 47 generator*.snapsnapshots – regenerated entity-builder output.InlineFragmentOrderTest,RecursiveEntitySelfReferenceTest(+ generated clients) and two facts inTestGeneration.cs.Verification
CodeGeneration.CSharp.Tests: 193 passed, 1 skipped (incl. all integration clients + generator snapshots).CodeGeneration.Razor.Tests: 1 passed.CodeGeneration.Tests: 29 passed, 1 skipped.StrawberryShake.Core.Tests: 210 passed.Fixes ChilliCream#8063
— Claude
🤖 Generated with Claude Code