Linq handles a variable OrderBy

I need to support a variable number of Orderby members in a Linq (to Entity) expression. That is, my function will accept a list of properties by which data should be ordered. Properties can have both ascending and descending sortings. What is the best way to handle building a Linq query?

Thank!

+3
source share
4 answers

You should do something in this direction:

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);

You need to handle the case when less than 2 properties are specified.

+9
source

Following from Jay's answer , this can be made a good extension method:

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;

    }
}

Usage becomes quite simple, given the test object:

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

Then it is possible:

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);
}

Conclusion:

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

UPDATE

OrderByMany SortOrder, .

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

, , # 4, .

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);
}
+3

, OrderBy.

ThenBy .

+2

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; }
    }
}

In my specific application, I have a base repository class that accepts IQueryable and converts it to an ObservableCollection. In this method, I use the SortExpression class:

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;
}        

Here is the method used:

var repository = new ContactRepository(UnitOfWork);
return repository.GetCollection(
                    new SortExpression<Contact>(x => x.FirstName),
                    new SortExpression<Contact>(x => x.LastName));       
0
source

Source: https://habr.com/ru/post/1787494/


All Articles