0

I am retrieving an ordered IQueryable<Item> from another method (QueryItems). I would like to (conditionally) reverse the order of IQueryable<Item> before it is passed on.

public async Task<PagedList<Item>> GetItems(FilterParameters filterParameters)
{
    var items = QueryItems(filterParameters)

    if (filterParams.Descending)
    {
        //do something to reverse the order of the IQueryable
    }

    return await PagedList<Item>
        .CreateAsync(items, filterParams.PageNumber, filterParams.PageSize)
}

I cannot do var reversed = items.ToList().Reverse; items = reversed.AsQueryable() because then it (apparently) cannot be used in asynchronous tasks.

When I try to do items = items.Reverse() I get the following error:

System.NotImplementedException: Remotion.Linq.Clauses.ResultOperators.ReverseResultOperator

Mostly because I'm lazy, I'd rather not go into the QueryItems method to implement items.OrderByDescending() as that would require a bunch of if/else statements.

So... how do you reverse the order of an IQueryable?

Edit - Inclde PagedList.CreateAsync(...)

public class PagedList<T> : List<T>
{
    public int CurrentPage { get; set; }
    public int TotalPages { get; set; }
    public int PageSize { get; set; }
    public int TotalCount { get; set; }

    public PagedList(List<T> items, int count, int pageNumber, int pageSize)
    {
        TotalCount = count;
        PageSize = pageSize;
        CurrentPage = pageNumber;
        TotalPages = (int) Math.Ceiling(count / (double) pageSize);
        this.AddRange(items);
    }

    public static async Task<PagedList<T>> CreateAsync(IQueryable<T> source, int pageNumber, int pageSize)
    {
        var count = await source.CountAsync();
        var items = await source.Skip((pageNumber - 1) * pageSize).Take(pageSize).ToListAsync();
        return new PagedList<T>(items, count, pageNumber, pageSize);
    }
}
JED
  • 1,538
  • 2
  • 19
  • 47
  • You can attach an outer `OrderByDescending` to what `QueryItems` returns, but it's probably not good from performance point of view, and you will need full knowledge about what sorting levels `QueryItems` applied anyway. So you would need a bunch of `If`s somewhere, at the point where you initially apply your sorting. – GSerg Aug 21 '18 at 16:41
  • Possible duplicate of [What's the difference between IQueryable and IEnumerable](https://stackoverflow.com/questions/2433306/whats-the-difference-between-iqueryable-and-ienumerable) – Tetsujin no Oni Aug 21 '18 at 16:52
  • 1
    This is fundamentally about the difference between a realized collection and IQueryable. – Tetsujin no Oni Aug 21 '18 at 16:52
  • why could you not use .OrderByDescending() – Seabizkit Aug 21 '18 at 16:57
  • @Seabizkit In my `QueryItems` method, I am already ordering the list. If I do something like `items = items.OrderByDescending(item => item.Id), then the ordering I did within the previous method is overwritten. I was really just looking for an easy way to flip it around after it'd already been ordered. – JED Aug 21 '18 at 17:07
  • @GSerg It seems you're right. I guess I'll have to get over my laziness and go modify the `QueryItems` method. Boo! – JED Aug 21 '18 at 17:08
  • Why not do a .ToList() and maybe a .Reverse()? – Hans Kesting Aug 21 '18 at 17:21
  • @HansKesting That would make doing it async less than useful, no? – NetMage Aug 21 '18 at 18:24
  • @JED sorry but i still do not understand... I'm assuming you mean "how do you append an additional order on and ordered list when type is IQueriable as `thenby` is not given til you start with `OrderBy` or `OrderByDescending`." is that accurate? – Seabizkit Aug 21 '18 at 19:40

2 Answers2

2

As pointed out in the comments below, you almost certainly are wanting to reverse the order of the whole query, such that the last page becomes the first and vice versa. For that, you have no choice but to use OrderByDescending. However, you can factor out your actual order by clause to reduce code duplication. For example:

Expression<Func<Item,int>> orderBy = o => o.Id;

Note: the int type param above corresponds with the type of the property in the expression. Change Id on the right hand side and int on the left hand side accordingly, based on what you actually want to order by.

Then:

items = filterParams.Descending
    ? items.OrderByDescending(orderBy)
    : items.OrderBy(orderBy);
Chris Pratt
  • 232,153
  • 36
  • 385
  • 444
  • That's not a bad idea. Let me edit my question to show the `PagedList.CreateAsync` method. I could probably do `Reverse()` on the `items` property of the returned page list. (btw, I didn't down-vote your answer) – JED Aug 21 '18 at 18:20
  • So, yeah. You can just call `pagedList.Reverse()`, since your `PagedList` is just a `List`. – Chris Pratt Aug 21 '18 at 18:27
  • 1
    @JED, Don't you need the full query reversed and not the page, otherwise your just be getting ordered pages out of order, If your going to reverse the items you probably want to grab the last page instead? – johnny 5 Aug 21 '18 at 18:29
  • @johnny5: Oh. Good point. I just got focused on being able to reverse the items and didn't think about the overall intent. If you need to reverse the order in the query itself, then your only options is to branch and use `OrderByDescending` instead. – Chris Pratt Aug 21 '18 at 18:32
  • @ChrisPratt Branching for `OrderByDescending` is probably the way to go, but if this could still work if they just added a total page count to the Page class and inverse the page and do reverse which should have the same affect. – johnny 5 Aug 21 '18 at 18:37
1

How about writing an ExpressionVisitor and using InterceptedQuery to re-write all OrderBy and ThenBy to OrderByDescending and ThenByDescending and vice-versa?

NetMage
  • 26,163
  • 3
  • 34
  • 55
  • That seems like overkill, when they can just add an if statement or two when they create the query in the first place – johnny 5 Aug 21 '18 at 18:38
  • I try not to assume the original question explains all the reasons not answering it might be the best solution. What if the query creation is done by third-party or provided code that can't be changed? – NetMage Aug 21 '18 at 18:49
  • Good point, I can't tell what they're using internally but this would work no matter what they're using – johnny 5 Aug 21 '18 at 19:15