AutoMapper / AutoMapper.Extensions.OData

Creates LINQ expressions from ODataQueryOptions and executes the query.
MIT License
140 stars 38 forks source link

GetQueryAsync ignores existing entity OrderBy expression if $orderby is not present in ODataQueryOptions but $skip and/or $top are #213

Open Kizzik opened 1 month ago

Kizzik commented 1 month ago

Version: 5.0.0 (EFCore)

Expected behavior

Results are sorted using the .OrderBy expression.

Actual behavior

A default sort order is applied.

Steps to reproduce

Make an .OrderBy call before .GetQueryAsync, with $top and/or $skip specified. e.g.:

// url: https://myhost/api/myEntities?$top=20&$skip=0
var baseQuery = context.MyEntities.OrderBy(e => /* custom sort */);
var query = await baseQuery.GetQueryAsync(mapper, opts);

Details

I have an ordering requirement that I can't feasibly represent using OData, so my intent is to order the IQueryable before calling GetQueryAsync.

This works if I don't use OData $skip or $top, but if either of those are specified then the custom ordering is overridden by the GetQueryableMethod > GetDefaultOrderByCall call.

I've tried doing the .OrderBy call after .GetQueryAsync, however if the expression used for my custom order includes navigation properties/children that aren't selected/expanded in the ODataQueryOptions (which I don't want, as I don't want to return the sorting data), then it fails.

Is there a way to bypass the initialisation of the default ordering, or have it detect that we already have an OrderBy call in the expression and use that in lieu of seeking a default? My current solution involves creating a new instance of ODataQueryOptions with top/skip removed, applying my custom sort, calling GetQueryAsync, then applying top/skip myself, but it's an ugly hack.

BlaiseD commented 1 month ago

I think there was a PR to do exactly that i.e. if there's a Skip but no OrderBy then add one. Adding a setting to ODataSattings something like SkipDefaultOrderBy should work.