Reputation: 6955
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?
Upvotes: 22
Views: 24433
Reputation: 10562
This will work:
YourCollection.Orderby(item => item.Property1).ThenBy(item => item.Property2);
Upvotes: 2
Reputation: 564413
OrderBy returns a new IEnumerable, so you need to do something like:
IEnumerable<Data> results
= query.OrderBy(item => item.GetType().GetProperty(sortExpressions[i].Name));
Upvotes: 5
Reputation: 8934
The query is not mutable, so OrderBy returns a new object. You need to make the same call, but add "query =" to the beginning.
query = query.OrderBy(item => item.GetType().GetProperty(sortExpressions[i].Name));
Upvotes: 2
Reputation: 1500555
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;
}
Upvotes: 27
Reputation: 61795
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]
Upvotes: 2
Reputation: 45117
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.
Upvotes: 2