2

Question:

I have a generic list, like this:

System.Collections.Generic.List<Question> myquestions = new System.Collections.Generic.List<Question>();

And I have a paging example, using a LINQ table acquried from database, doing this for paging:

var questions = context.Questions
    .OrderBy(sidx + " " + sord)
    .Skip(pageIndex * pageSize)
    .Take(pageSize);

Right now, for paging my populated from code list, I have:

var questionss = myquestions
    .OrderBy(x => x.Id)
    .Skip(pageIndex * pageSize)
    .Take(pageSize);

And what I want is being able to order "myquestions" by a string as in the above example. Is that possible?

hangy
  • 10,460
  • 6
  • 40
  • 61
Stefan Steiger
  • 68,404
  • 63
  • 337
  • 408

5 Answers5

3

You can chain the extension methods in separate statements thanks to the lazy execution, which means that you can add different sorting depending on a condition:

IOrderedQueryable<Question> sorted;
switch (sort) {
  case "Id":  sorted = myquestions.OrderBy(x => x.Id);
  case "Name": sorted = myquestions.OrderBy(x => x.Name);
  case "Size": sorted = myquestions.OrderBy(x => x.Size);
  case "Id Desc":  sorted = myquestions.OrderByDescending(x => x.Id);
  case "Name Desc": sorted = myquestions.OrderByDescending(x => x.Name);
  case "Size Desc": sorted = myquestions.OrderByDescending(x => x.Size);
  default: throw new NotImplementedException();
}
var questions = sorted.Skip(pageIndex * pageSize).Take(pageSize);

You can even add a secondary sorting (or as many as you like, after the first they are all the same):

IOrderedQueryable<Question> sorted;
switch (sort) {
  case "Id":  sorted = myquestions.OrderBy(x => x.Id);
  case "Name": sorted = myquestions.OrderBy(x => x.Name);
  case "Size": sorted = myquestions.OrderBy(x => x.Size);
  case "Id Desc":  sorted = myquestions.OrderByDescending(x => x.Id);
  case "Name Desc": sorted = myquestions.OrderByDescending(x => x.Name);
  case "Size Desc": sorted = myquestions.OrderByDescending(x => x.Size);
  default: throw new NotImplementedException();
}
switch (sort2) {
  case "Id":  sorted = sorted.ThenBy(x => x.Id);
  case "Name": sorted = sorted.ThenBy(x => x.Name);
  case "Size": sorted = sorted.ThenBy(x => x.Size);
  case "Id Desc":  sorted = sorted.ThenByDescending(x => x.Id);
  case "Name Desc": sorted = sorted.ThenByDescending(x => x.Name);
  case "Size Desc": sorted = sorted.ThenByDescending(x => x.Size);
}
var questions = sorted.Skip(pageIndex * pageSize).Take(pageSize);
Guffa
  • 640,220
  • 96
  • 678
  • 956
3

Bahaha, now I'm gonna cry:

Trying to implement dtb's solution, I saw the problem:

OrderByField<T>(IQueryable<T> q,

Returns IQueryable while inputting IQueryable... lol bs. So, the real question is how to convert List to be iQueryable.

Which, in fact is dead simple:

var list = new List<T>(); 
var queryable = list.AsQueryable(); 

then

queryable.OrderBy(sidx + " " + sord);

So the final solution is as trivial as:

myquestions.AsQueryable().OrderBy(sidx + " " + sord).Skip(pageIndex * pageSize).Take(pageSize);

Which also means Linq.Table implements IQueryable, which makes sense IMHO.

Edit:
Note:
This requires reference to: Dynamic.dll (not System.Dynamic.dll) as well as

using System.Linq.Dynamic;

If you don't reference Dynamic.dll, or don't declare the using directive, you'll get an error on AsQueryable().

Stefan Steiger
  • 68,404
  • 63
  • 337
  • 408
  • This is the best answer by miles. If you don't have System.Linq.Dynamic, just use nuget to get it. – MGOwen Jul 13 '13 at 01:00
2

Please check my answer to another question on how to build a custom OrderBy predicate using Expression from a given String.

Community
  • 1
  • 1
Cheng Chen
  • 39,413
  • 15
  • 105
  • 159
1

One option is to create a dictionary from each possible ordering string to an appropriate delegate. For example:

Dictionary<string, Func<IEnumerable<Question>, IEnumerable<Question>> orderings =
    new Dictionary<string, Func<IEnumerable<Question>, IEnumerable<Question>>()
{
    { "Id",  questions => questions.OrderBy(x => x.Id) },
    { "Title",  questions => questions.OrderBy(x => x.Title) },
    { "Answer",  questions => questions.OrderBy(x => x.Answer) },
    { "Id Desc",  questions => questions.OrderByDescending(x => x.Id) },
    { "Title Desc",  questions => questions.OrderByDescending(x => x.Title) },
    { "Answer Desc",  questions => questions.OrderByDescending(x => x.Answer) },
};

Then you can find that mapping at execution time, and apply the function.

Jon Skeet
  • 1,261,211
  • 792
  • 8,724
  • 8,929
  • I get the idea, but it's gonna create an awful lot of typing, plus it will be a 2d dictionary, because it also needs sort order ASC|DESC. Automatizing this would probably require reflection. – Stefan Steiger Mar 02 '11 at 08:17
  • @Quandary: You could always sort ascending and use `Reverse` if you want a descending list. I'd guess either the Linq query optimizer or the SQL server are smart enough to optimize that away and do a descending sort instead. – Niki Mar 02 '11 at 08:19
  • I'll remember that if I need very custom search order. – Stefan Steiger Mar 02 '11 at 09:15
  • @JonSkeet, does this still hold for you? – Rod Dec 09 '15 at 21:52
  • @Rod: What in particular? – Jon Skeet Dec 09 '15 at 22:09
  • Your dictionary answer. I'm trying to use the dynamic sortExpression string with linq. – Rod Dec 09 '15 at 22:29
  • @Rod: Well yes, as noted on the answer you accepted, you weren't doing anything with the result. – Jon Skeet Dec 09 '15 at 22:32
  • Ok thanks, I was just wondering if there was another concise way to do it. No biggie. – Rod Dec 09 '15 at 22:35
  • @Rod: It would have been helpful if you'd said that to start with. This is still the way I would personally do it. – Jon Skeet Dec 09 '15 at 22:36
1

You could look at Dynamic Linq. IIRC it was a sample that shipped with VS2008, I'm not 100% sure if it's still included in VS2010.

Niki
  • 15,188
  • 5
  • 41
  • 72