Как я могу использовать linq для сортировки по нескольким полям?
Я создаю макет источника данных, который я хочу передать в список SortExpressions.
public SortExpression(string name, SortDirection direction)
{
this.name = name;
this.direction = direction;
}
Обновить с помощью кода Jon Skeet, а также всего класса. GetData() просто заполняет объект х количеством записей.
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>();
}
}
Кажется, ничего не делает. Компилирует, нет ошибок, просто нет. Любые идеи?
Ответы
Ответ 1
Есть две проблемы. Первое - это то, о чем другие говорили - вам нужно использовать значение, возвращаемое OrderBy
и т.д. Во-вторых, каждый раз вы вызываете OrderBy
, добавляя новый "первичный" заказ. Вы действительно хотите ThenBy
после первого заказа. К сожалению, это довольно уродливо. Это все еще довольно уродливо после рефакторинга, но не так уж плохо...
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;
}
Ответ 2
OrderBy возвращает новый IEnumerable, поэтому вам нужно сделать что-то вроде:
IEnumerable<Data> results
= query.OrderBy(item => item.GetType().GetProperty(sortExpressions[i].Name));
Ответ 3
Операторы OrderBy/OrderByDescending работают как String.ToUpper(), то есть они берут то, на что вы ссылаетесь, и получаете "копию", которая имеет то, о чем вы просили.
Другими словами, вместо того, чтобы говорить:
query.Orderby(item->item.X)
вы должны сделать
query = query.Orderby(item->item.X)
или
sortedResult = query.Orderby(item->item.X)
[И как указывает Джон Скит, используйте ThenBy
/ThenByDescending
как в своем ответе]
Ответ 4
Запрос не изменен, поэтому OrderBy возвращает новый объект. Вам нужно сделать тот же вызов, но добавьте "query =" в начало.
query = query.OrderBy(item => item.GetType().GetProperty(sortExpressions[i].Name));
Ответ 5
OrderBy on IEnumerable возвращает возвращает IOrderedEnumerable. Он не сортирует их по очереди. Так что получите возвращаемое значение из вашего .OrderBy, и все будет хорошо.
Ответ 6
Это будет работать:
YourCollection.Orderby(item => item.Property1).ThenBy(item => item.Property2);