I'm creating a mock data source that I want to be able to pass in a list of SortExpressions on.
public SortExpression(string name, SortDirection direction)
{
this.name = name;
this.direction = direction;
}
Update with Jon Skeet's code and also the entire class. GetData() is just populating the object with x number of records.
public class Data
{
public int Id { get; set; }
public Guid gId { get; set; }
public string Name { get; set; }
public string Phone { get; set; }
public string Address { get; set; }
public DateTime Created { get; set; }
public string SortMe { get; set; }
public static List<Data> GetFakeData(int start, int numberToFetch, IList<SortExpression> sortExpressions, IList<FilterExpression> filterExpressions, out int totalRecords)
{
DataCollection items = GetData();
IEnumerable<Data> query = from item in items select item;
bool sortExpressionsExist = sortExpressions != null;
if (sortExpressionsExist)
{
// Won't be read in the first iteration; will be written to
IOrderedEnumerable<Data> orderedQuery = null;
for (int i = 0; i < sortExpressions.Count; i++)
{
// Avoid single variable being captured: capture one per iteration.
// Evil bug which would be really hard to find :)
int copyOfI = i;
// Tailor "object" depending on what GetProperty returns.
Func<Data, object> expression = item =>
item.GetType().GetProperty(sortExpressions[copyOfI].Name);
if (sortExpressions[i].Direction == SortDirection.Ascending)
{
orderedQuery = (i == 0) ? query.OrderBy(expression)
: orderedQuery.ThenBy(expression);
}
else
{
orderedQuery = (i == 0) ? query.OrderByDescending(expression)
: orderedQuery.ThenByDescending(expression);
}
}
query = orderedQuery;
}
bool filterExpressionsExist = filterExpressions != null;
if (filterExpressionsExist)
{
foreach (var filterExpression in filterExpressions)
{
query.Where(item => item.GetType().GetProperty(filterExpression.ColumnName).GetValue(item, null).ToString().Contains(filterExpression.Text));
}
}
totalRecords = query.Count();
return query.Skip(start).Take(numberToFetch).ToList<Data>();
}
}
Doesn't seem to be doing anything. Compiles, no errors, just no sort. Any ideas?
Always make use of ThenBy() after OrderBy() because OrderBy() returns an IOrderedEnumerable which then exposes the methods ThenBy() and ThenByDescending() . This means that we can OrderBy on multiple fields by chaining OrderBy() and ThenBy() together.
Try OrderBy(x => x. Col1). ThenBy(x => x. Col2) .
For LINQ to Objects, it's a stable quicksort that is used.
In LINQ, the OrderBy operator is used to sort the list/ collection values in ascending order. In LINQ, if we use order by the operator by default, it will sort the list of values in ascending order. We don't need to add any ascending condition in the query statement.
There are two problems. The first is the one others have alluded to - you need to use the value returned by OrderBy
etc. The second is that each time you call OrderBy
, that's adding a new "primary" ordering. You really want ThenBy
after the first ordering has been applied. That makes it pretty ugly, unfortunately. It's still pretty ugly after a refactoring, but not too bad...
IEnumerable<Data> query = from item in items select item;
if (sortExpressionsExist)
{
// Won't be read in the first iteration; will be written to
IOrderedEnumerable<Data> orderedQuery = null;
for (int i = 0; i < sortExpressions.Count; i++)
{
// Avoid single variable being captured: capture one per iteration.
// Evil bug which would be really hard to find :)
int copyOfI = i;
// Tailor "object" depending on what GetProperty returns.
Func<Data, object> expression = item =>
item.GetType()
.GetProperty(sortExpressions[copyOfI].Name)
.GetValue(item, null);
if (sortExpressions[i].Direction == SortDirection.Ascending)
{
orderedQuery = (i == 0) ? query.OrderBy(expression)
: orderedQuery.ThenBy(expression);
}
else
{
orderedQuery = (i == 0) ? query.OrderByDescending(expression)
: orderedQuery.ThenByDescending(expression);
}
}
query = orderedQuery;
}
OrderBy returns a new IEnumerable, so you need to do something like:
IEnumerable<Data> results
= query.OrderBy(item => item.GetType().GetProperty(sortExpressions[i].Name));
OrderBy on IEnumerable returns returns an IOrderedEnumerable. It does not sort them in line. So get the return value from your .OrderBy and you will be fine.
The OrderBy/OrderByDescending 'operators' work like String.ToUpper(), i.e., they take the thing you invoke it on, and yield a 'copy' that has what you asked for.
In other words, instead of saying:
query.Orderby(item->item.X)
you should do
query = query.Orderby(item->item.X)
or
sortedResult = query.Orderby(item->item.X)
[And as Jon Skeet points out, use ThenBy
/ThenByDescending
as in his answer]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With