Skip to content

Add JSON column support for default ordering - #113

Merged
SimonCropp merged 1 commit into
mainfrom
feature/json-column-ordering
Aug 1, 2026
Merged

Add JSON column support for default ordering#113
SimonCropp merged 1 commit into
mainfrom
feature/json-column-ordering

Conversation

@SimonCropp

@SimonCropp SimonCropp commented Aug 1, 2026

Copy link
Copy Markdown
Owner

OrderBy/ThenBy now accept a property path, so a property of a ToJson() mapped column can be used for default ordering.

builder.Entity<Order>()
    .OwnsOne(_ => _.Metadata, _ => _.ToJson());

builder.Entity<Order>()
    .OrderByDescending(_ => _.Metadata.Priority)
    .ThenBy(_ => _.Reference);

EF Core translates the path to a read of the JSON document:

ORDER BY CAST(JSON_VALUE([o].[Metadata], '$.Priority') AS int) DESC, [o].[Reference]

Paths of any depth work (_ => _.Info.Audit.Modified becomes '$.Audit.Modified'), and JSON properties mix with ordinary columns in the same chain. Everything else composes as before: the ordering is applied before Skip/Take/First, explicit ordering in a query still takes precedence, and inheritance, cross context conflict detection and redundant ordering detection all understand the dotted path.

Implementation

  • New PropertyPath resolves a member chain rooted at the lambda parameter. OrderByClause walks that chain instead of building a single Expression.Property.
  • ClauseMetadata stores the dotted path, so derived type replay, conflict detection across contexts and RedundantOrder keep working unchanged.

Two deliberate limits

Indexes are skipped for JSON paths. A JSON property is not a column of the entity's table, so there is nothing to name in HasIndex. This follows the existing string column behaviour: the index is skipped, the ordering still applies. A composite ordering is skipped whole when any one of its clauses reaches into JSON.

JSON collections keep document order. Include(_ => _.Tags.OrderBy(...)) over a ToJson() collection throws InvalidOperationException on a tracking query; it only works under AsNoTracking. Auto applying ordering there would break queries that work today, so IncludeOrderingApplicator now leaves JSON mapped collections alone. Without that guard, a CLR type used both as a regular entity and as a JSON collection elsewhere would hit exactly that failure.

Tests

Ten tests in JsonColumnTests.cs cover ordering, nesting, mixed chains, Take, index skipping, redundancy detection, requireOrderingForAllEntities against JSON owned types, and that a tracked Include over a JSON collection still works. Three Verify snapshots lock in the generated SQL.

Full suite passes in Release (139 tests), verified against SQL Server via LocalDb.

OrderBy/ThenBy now accept a property path, so a property of a ToJson()
mapped column can be used for default ordering. EF Core translates the
path to a read of the JSON document:

    builder.Entity<Order>()
        .OrderByDescending(_ => _.Metadata.Priority);

    ORDER BY CAST(JSON_VALUE([o].[Metadata], '$.Priority') AS int) DESC

Paths of any depth work, and JSON properties mix with ordinary columns in
the same chain. Cross context conflict detection, inherited ordering, and
redundant ordering detection all understand the dotted path.

Index creation is skipped for an ordering that reaches into JSON, since a
JSON property is not a column of the entity's table. This follows the
existing string column behaviour: the index is skipped, the ordering
still applies.

JSON mapped collections are left in document order. EF Core throws on an
ordered Include over one in a tracking query, so ordering them would
break queries that work today.
@SimonCropp SimonCropp added this to the 1.3.0 milestone Aug 1, 2026
@SimonCropp
SimonCropp merged commit 9bcb5ad into main Aug 1, 2026
7 checks passed
@SimonCropp
SimonCropp deleted the feature/json-column-ordering branch August 1, 2026 12:22
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.

1 participant