Linq обрабатывает переменную номер OrderBy - PullRequest
3 голосов
/ 24 января 2011

Мне нужно поддерживать переменное количество терминов Orderby в операторе Linq (to Entity).То есть моя функция примет список свойств, по которым должны быть упорядочены данные.Свойства могут иметь как восходящие, так и нисходящие сортировки.Каков наилучший способ обработки запроса Linq?

Спасибо!

Ответы [ 4 ]

9 голосов
/ 24 января 2011

Вы должны быть в состоянии что-то сделать по следующим направлениям:

public IEnumerable<MyType> DoSomething(params Expression<Func<MyType,object>>[] properties)
 {
     var query = // create LINQ query that returns IQueryable<MyType>
     query = query.OrderBy(properties.First());

     foreach (var property in properties.Skip(1))
     {
         query = query.ThenBy(property);
     }
 }

 …

 var results = DoSomething(() => x.Age, () => x.Height, () => x.LastName);

Вам нужно будет обработать случай, когда указано менее 2 свойств.

3 голосов
/ 24 января 2011

Исходя из ответа Джея , это можно превратить в хороший метод расширения:

public static class EnumerableExtensions
{
    public static IEnumerable<T> OrderByMany<T>(this IEnumerable<T> enumerable, 
        params Expression<Func<T, object>>[] expressions)
    {
        if (expressions.Length == 1)
            return enumerable.OrderBy(expressions[0].Compile());

        var query = enumerable.OrderBy(expressions[0].Compile());
        for (int i = 1; i < expressions.Length;i++)
        {
            query = query.ThenBy(expressions[i].Compile());
        }
        return query;

    }
}

Использование становится довольно простым, учитывая объект теста:

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

Это возможно:

var people = new Person[]
                    {
                        new Person() {Name = "John", Age = 40},
                        new Person() {Name = "John", Age = 20},
                        new Person() {Name = "Agnes", Age = 11}
                    };

foreach(var per in  people.OrderByMany(x => x.Name, x => x.Age))
{
    Console.WriteLine("{0} Age={1}",per.Name,per.Age);
}

Выход:

Agnes Age=11
John Age=20
John Age=40

ОБНОВЛЕНИЕ

Вы можете добавить еще одну перегрузку OrderByMany метод поддержки SortOrder, хотя он довольно быстро становится неуклюжим.Лично я бы просто использовал синтаксис

var query = from p 
            in people
            order by Name, Age descending;

Однако для записи, по крайней мере в C # 4, я бы выполнил перегрузку, используя enum & tuple.

public enum SortOrder
{
    Ascending, 
    Descending
}

и дополнительная перегрузка:

public static IEnumerable<T> OrderByMany<T>(this IEnumerable<T> enumerable,
    params Tuple<Expression<Func<T, object>>,SortOrder>[] expressions)
{

    var query = (expressions[0].Item2 == SortOrder.Ascending)
                    ? enumerable.OrderBy(expressions[0].Item1.Compile())
                    : enumerable.OrderByDescending(expressions[0].Item1.Compile());

    for (int i = 1; i < expressions.Length; i++)
    {
        query = expressions[i].Item2 == SortOrder.Ascending
                    ? query.ThenBy(expressions[i].Item1.Compile())
                    : query.ThenByDescending(expressions[i].Item1.Compile());
    }
    return query;

}

Использование становится неуклюжим и трудно читаемым:

foreach (var per in people.OrderByMany(
                    new Tuple<Expression<Func<Person, object>>, SortOrder>(x => x.Age, SortOrder.Descending), 
                    new Tuple<Expression<Func<Person, object>>, SortOrder>(x => x.Name, SortOrder.Ascending)))
{
    Console.WriteLine("{0} Age={1}", per.Name, per.Age);
}
2 голосов
/ 24 января 2011

Чтобы отсортировать по произвольному свойству, вам нужно построить дерево выражений для передачи на OrderBy.

Чтобы отсортировать по произвольному количеству свойств, вам нужно вызвать ThenBy в цикле.

0 голосов
/ 01 февраля 2012

Мне нравится идея Jamiec, но я ненавижу использовать Tuples, потому что синтаксис уродлив. Поэтому я создал небольшой класс, который инкапсулирует Tuple и предоставляет геттеры для свойств Item1 и Item2 с лучшими именами переменных.

Также обратите внимание, что я использовал порядок сортировки по возрастанию по умолчанию, поэтому вам нужно только указать SortOrder, если вы хотите сортировать по убыванию.

public class SortExpression<T>
{
    private Tuple<Expression<Func<T, object>>, SortOrder> tuple;        

    public SortExpression( Expression<Func<T, object>> expression, SortOrder order =SortOrder.Ascending )
    {
        tuple = new Tuple<Expression<Func<T,object>>, SortOrder>(expression, order);
    }

    public Expression<Func<T, object>> Expression {
        get { return tuple.Item1; }
    }

    public SortOrder Order {
        get { return tuple.Item2; }
    }
}

В моем конкретном приложении у меня есть базовый класс репозитория, который принимает IQueryable и преобразует его в ObservableCollection. В этом методе я использую класс SortExpression:

public ObservableCollection<T> GetCollection(params SortExpression<T>[] sortExpressions) {
    var list = new ObservableCollection<T>();
    var query = FindAll();

    if (!sortExpressions.Any()) {
        query.ToList().ForEach(list.Add);
        return list;
    }

    var ordered = (sortExpressions[0].Order == SortOrder.Ascending)
        ? query.OrderBy(sortExpressions[0].Expression.Compile())
        : query.OrderByDescending(sortExpressions[0].Expression.Compile());

    for (var i = 1; i < sortExpressions.Length; i++) {
        ordered = sortExpressions[i].Order == SortOrder.Ascending
            ? ordered.ThenBy(sortExpressions[i].Expression.Compile())
            : ordered.ThenByDescending(sortExpressions[i].Expression.Compile());
    }

    ordered.ToList().ForEach(list.Add);
    return list;
}        

Вот используемый метод:

var repository = new ContactRepository(UnitOfWork);
return repository.GetCollection(
                    new SortExpression<Contact>(x => x.FirstName),
                    new SortExpression<Contact>(x => x.LastName));       
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...