Skip to content

OnAfterSortingApplied can no longer append a default sort order when the IQueryable ends with a Select operator #10419

Description

@fschmied

Product

Hot Chocolate

Version

16.6.6

Link to minimal reproduction

https://gist.github.com/fschmied/254d7bf689dbd912e5279e70c1e15670

Steps to reproduce

The linked repro sample uses the pattern from https://chillicream.com/blog/2024-08-30-hot-chocolate-14#pagination to control the default sort order in our GraphQL queries, modified to have a query that ends with a Select operator:

[UseSorting]
public static async IQueryable<BrandDto> GetBrands(
    IBrandRepository brandRepository,
    ISortingContext sorting)
{
    // this signals that the expression was not handled within the resolver
    // and the sorting middleware should take over.
    sorting.Handled(false);

    sorting.OnAfterSortingApplied<IQueryable<BrandDto>>(
        static (sortingApplied, query) =>
        {
            if (sortingApplied && query is IOrderedQueryable<BrandDto> ordered)
            {
                return ordered.ThenBy(b => b.Id);
            }

            return query.OrderBy(b => b.Id);
        });

    return context.Brands.Select(b => new BrandDto{ Id = b.Id, Name = b.Name }); // Note the Select!
}

When running it with Hot Chocolate 16.6.6, e.g., using the supplied Invoke-Repro.ps1, it will fail with an ArgumentException.
When changing the csproj to reference Hot Chocolate 15.1.16 instead, it works.

What is expected?

The output should be this:

{
  "data": {
    "brands": [
      {
        "id": 3,
        "name": "Alpha"
      },
      {
        "id": 1,
        "name": "Beta"
      },
      {
        "id": 2,
        "name": "Beta"
      }
    ]
  }
}

What is actually happening?

An ArgumentException is thrown. The output is this:

{
  "errors": [
    {
      "message": "Unexpected Execution Error",
      "path": [
        "brands"
      ],
      "extensions": {
        "exception": {
          "message": "Expression of type 'System.Linq.IQueryable`1[BrandDto]' cannot be used for parameter of type 'System.Linq.IOrderedQueryable`1[BrandDto]' of method 'System.Linq.IOrderedQueryable`1[BrandDto] ThenBy[BrandDto,Int64](System.Linq.IOrderedQueryable`1[BrandDto], System.Linq.Expressions.Expression`1[System.Func`2[BrandDto,System.Int64]])' (Parameter 'arg0')",
          "stackTrace": "   at System.Dynamic.Utils.ExpressionUtils.ValidateOneArgument(MethodBase method, ExpressionType nodeKind, Expression arguments, ParameterInfo pi, String methodParamName, String argumentParamName, Int32 index)\r\n   at System.Linq.Expressions.Expression.Call(Expression instance, MethodInfo method, Expression arg0, Expression arg1)\r\n   at System.Linq.Queryable.ThenBy[TSource,TKey](IOrderedQueryable`1 source, Expression`1 keySelector)\r\n   at Query.<>c.<GetBrands>b__0_0(Boolean sortingApplied, IQueryable`1 query) in C:\\Users\\fschmied\\Desktop\\Temp\\2026-09-22 Hot Chocolate 16 Default Sort Definition Bug\\Program.cs:line 34\r\n   at HotChocolate.Data.Sorting.Expressions.QueryableSortProvider.ApplyPostActionToResult[TEntityType](Object input, Boolean sortingApplied, PostSortingAction`1 postAction)\r\n   at HotChocolate.Data.Sorting.Expressions.QueryableSortProvider.<>c__DisplayClass14_0`1.<CreateApplicatorAsync>b__0(IResolverContext context, Object input)\r\n   at HotChocolate.Data.Sorting.Expressions.QueryableSortProvider.QueryableQueryBuilder.Apply(IMiddlewareContext context)\r\n   at HotChocolate.Types.UnwrapFieldMiddlewareHelper.<>c__DisplayClass0_1.<<CreateDataMiddleware>b__1>d.MoveNext()\r\n--- End of stack trace from previous location ---\r\n   at HotChocolate.Execution.Processing.Tasks.ResolverTask.ExecuteResolverPipelineAsync(CancellationToken cancellationToken)\r\n   at HotChocolate.Execution.Processing.Tasks.ResolverTask.TryExecuteAsync(CancellationToken cancellationToken)"
        }
      }
    }
  ],
  "data": null
}

Relevant log output

System.ArgumentException: Expression of type 'System.Linq.IQueryable`1[BrandDto]' cannot be used for parameter of type 'BrandDto' of method 'System.Linq.IOrderedQueryable`1[BrandDto] ThenBy[BrandDto,Int64](System.Linq.IOrderedQueryable`1[BrandDto], System.Linq.Expressions.Expression`1[System.Func`2[BrandDto,System.Int64]])' (Parameter 'arg0')

Additional context

We're using the pattern from https://chillicream.com/blog/2024-08-30-hot-chocolate-14#pagination to control the default sort order in our GraphQL queries. Unfortunately, this stopped working when we upgraded from Hot Chocolate 15 to 16 because HotChocolate now adds the OrderBy/ThenBy operators before the Select.

We already traced this back to the change made by #9191.

For now, we worked around this problem by suffixing our queries with a Where(_ => true) operator, but of course this is not a nice solution.

Please fix, for example by avoiding the optimization if an "OnAfterSortingApplied" callback is given.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions