Repository navigation
fix: refuse ordering a value object with its own CompareTo that a projection boxed first - #217
Merged
CaffeinatedCoder merged 3 commits intoOct 31, 2026
Conversation
…jection boxed first Select(r => (object)r.Rank).Max(), .Min(), .Order(), .OrderDescending() and .OrderBy(x => x) over a value object whose ordering is its own CompareTo were not refused: EF dropped the cast and answered the column's order, Max 3 and Order 1, 2, 3 where memory answered 1 and 3, 2, 1 for a descending CompareTo (measured on SQLite). The Finder judged an ordering operator by its declared type, object here, and looked under a conversion only in a key selector's body, which 8f6af8b added for OrderBy(r => (object)r.Rank). The Finder now reads the types a sequence's elements have under their boxing: a Select's body unconverted, followed through the operators that pass elements on (Where, Take, Distinct, the orderings, Cast and OfType, Concat and its kind). An operator without a key, or whose key is the element itself, is refused by those types. Query filters go through the same Finder, so a filter that does this is refused when the model is built. The README says the cast changes nothing. Input class (EFT): an ordering operator over a value object, by where the conversion sits. Catalogue: QueryShapes' Op axis gains OrderBoxed, OrderByItselfBoxed, MinBoxed and MaxBoxed over every wrapped kind, converter and accessor of the column and nullable sites (green on SQLite, PostgreSQL's translation and SQL Server). Explicit rows hold OrderDescending, a record class, a navigation's Enumerable.Max, a Where and Take between the box and the operator, and a query filter. Sibling found: over a navigation, the same query threw a NullReferenceException inside EF rather than answering; it is refused before EF now. Reverting the source fails 33 of 11095 (24 catalogue rows, 9 explicit). Refs #206 Defect-Class: EFT Found-By: CA Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 30, 2026
…rojection's member
OrderBy(r => (object?)r.Optional ?? r.Rank) and
Select(r => new { Key = (object)r.Rank }).OrderBy(a => a.Key) over a
value object whose ordering is its own CompareTo were not refused: EF
dropped the cast and answered 1, 2, 3 where memory answered 3, 2, 1 for
a descending CompareTo (measured on SQLite at 3d2abfd). The Finder looked
under a conversion only at the top of a key's body or a Select's, so a
?? over the box, or a member of a projection that holds it, read as
object.
The Finder now reads the types a key or selector body yields under its
boxing: both sides of a ?? and a ?:, the elements for the lambda's
parameter (as before), and for a member of a projection, the expression
its new or member initializer set it from, found in the same lambda or
in the Select that built the elements, through nested projections and
the operators that pass elements on. The README, its claim and the
changelog line name both shapes.
Input class (EFT): an ordering operator over a value object, by where
the boxing sits and how the key reads it back. Siblings found by the
same probe and fixed here (VA): Max(a => a.Key) and
Select(a => a.Key).Max() over the projection, a nested anonymous member,
a member-init type, a Where between the projection and the ordering, a
?? inside the projection; each answered the column's order. A ?: key
failed translation on SQLite and is refused before it now. Over a
navigation the projected Max threw a NullReferenceException inside EF;
it is refused before EF. Catalogue: QueryShapes' Op axis gains
OrderByCoalescedBoxed, OrderByProjectedBoxed and MaxProjectedBoxed over
every kind, converter and accessor of the column and nullable sites.
Explicit rows hold the anonymous shapes, the navigation and a query
filter; controls over .Value and a generated ordering still translate.
Reverting the source fails 29: 18 catalogue rows, 11 explicit.
Refs #206
Defect-Class: EFT
Found-By: OR
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 31, 2026
…query's First carries Eight shapes still answered the column's order at d573eef, 1, 2, 3 (Max 3) where memory answers 3, 2, 1 (Max 1) for a descending CompareTo, measured on SQLite: a grouping's boxed key under OrderBy(g => g.Key), a grouping's boxed elements under g.Max(), GroupBy's, Join's and SelectMany's result selectors holding the boxed value, SelectMany of boxed values under Max(), and a subquery's or a navigation's boxed First() as a key. The Finder followed a lambda's parameter only through Select and the operators that pass elements on, and visited a nested operator, g.Max(), knowing nothing of its source. The Finder now knows what each lambda parameter stands for. Every LINQ operator binds its lambdas' parameters: to the source's elements for a selector, key or predicate; to each side's elements for Join and GroupJoin (the inner side whole for GroupJoin); to the collection's elements for SelectMany's result selector; to the key and the grouped elements for GroupBy's. A grouping's Key reads its key selector, and First, Single, Last, ElementAt and their OrDefault forms read the element of their source. The visitor carries these bindings into nested lambdas, so an operator inside one reads what its source was built from. The README, its claim and the changelog line name the new shapes. Input class (EFT): an ordering operator over a value object, by the operator whose lambda carries the boxed value to it. Sibling found by the same rows (VA): a GroupJoin result reading cs.First().Rank, refused before EF now. Loud and left as it is: a Tuple.Create projection, which EF does not translate. Catalogue: QueryShapes' Op axis gains OrderByGroupKeyBoxed and OrderByJoinedBoxed over every kind, converter and accessor of the column and nullable sites. Controls over .Value and a generated ordering, through a grouping, a join and a subquery, still translate. Reverting the source fails 21: 12 catalogue rows, 9 explicit. Refs #206 Defect-Class: EFT Found-By: RS Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 31, 2026
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.
Closes #206
Problem
Select(r => (object)r.Rank).Max(),.Min(),.Order(),.OrderDescending()and.OrderBy(x => x)over a value object whose ordering is its ownCompareTowere not refused: EF dropped the cast and answered the column's order,Max3 andOrder1, 2, 3 where memory answers 1 and 3, 2, 1 for a descendingCompareTo(measured on SQLite).HandWrittenOrderingInterceptor'sFinderjudged an ordering operator by its declared type,objecthere, and looked under a conversion only in a key selector's body (8f6af8b). The same held for a key that reads the boxed value back through??or a member of a projection:OrderBy(r => (object?)r.Optional ?? r.Rank)andSelect(r => new { Key = (object)r.Rank }).OrderBy(a => a.Key)answered 1, 2, 3 where memory answers 3, 2, 1 (reported by an outside review, measured here at3d2abfd). And through other operators' lambdas: a grouping's boxed key or elements,GroupBy's,Join's andSelectMany's result selectors, and a subquery's boxedFirst()as a key, all answering the column's order atd573eef.Change
The
Finderreads the types a sequence's elements have under their boxing: aSelect's body unconverted, followed through the operators that pass elements on (Where,Take,Distinct, the orderings,Cast/OfType,Concatand its kind). An operator without a key, or whose key is the element itself, is refused by those types. A key or selector body is read through both sides of a??and a?:, and a member of a projection is followed back to the expression itsnewor member initializer set it from, in the same lambda or in theSelectthat built the elements, through nested projections (d573eef). Every LINQ operator binds its lambdas' parameters to what they stand for (the source's elements, each side of a join,SelectMany's collection,GroupBy's key and elements); a grouping'sKeyreads its key selector,Firstand its kind read their source's element, and the visitor carries the bindings into nested lambdas, soSelect(g => g.Max())is refused too (8562de6). Query filters go through the sameFinder, so such a filter is refused when the model is built. The EF Core README says the cast changes nothing, through??, a projection's member, a grouping, a join or a subquery too (claimE05-ordering-boxed); changelog line under 2.3.1.Tests
QueryShapes'Opaxis gainsOrderBoxed,OrderByItselfBoxed,MinBoxedandMaxBoxedover every wrapped kind, converter and accessor of the column and nullable sites, green on SQLite, PostgreSQL's translation and SQL Server.HandWrittenOrderingTestsgains explicit rows:Max,Min,Order,OrderDescending,OrderBy(x => x), a record class, a navigation'sEnumerable.Max(which threw aNullReferenceExceptioninside EF before), aWhereandTakebetween the box and the operator, and a query filter.d573eef: theOpaxis gainsOrderByCoalescedBoxed,OrderByProjectedBoxedandMaxProjectedBoxed; explicit rows for a??and a?:key, an anonymous projection's member underOrderBy,Max(a => a.Key)andSelect(a => a.Key).Max(), a nested member, a member-init type, aWherein between, a??inside the projection, a navigation (aNullReferenceExceptioninside EF before) and a query filter; controls over.Valueand a generated ordering still translate. Reverting its source fails 29 (18 catalogue rows, 11 explicit). EF Core tests green (23,341 passed, SQL Server on azure-sql-edge), conventions green.8562de6: theOpaxis gainsOrderByGroupKeyBoxedandOrderByJoinedBoxed; explicit rows for a grouping's boxed key and elements,GroupBy's,Join's,GroupJoin's andSelectMany's result selectors,SelectManyunderMax, and a subquery's and a navigation's boxedFirst(); controls through a grouping, a join and a subquery over.Valueor a generated ordering still translate. Reverting its source fails 21 (12 catalogue rows, 9 explicit). EF Core tests green (24,258 passed), conventions green.Found by a sweep of
release/2.3.1@dbdd44efor the defect class of #191-#197.🤖 Generated with Claude Code