Linq处理可变数量的OrderBy

我需要在Linq(to Entity)语句中支持可变数量的Orderby术语。 也就是说,我的函数将接受数据应该在其上排序的属性列表。 属性可以具有升序或降序排序。 处理构造Linq查询的最佳方法是什么?

谢谢!

你应该能够沿着这些方向做点什么:

public IEnumerable DoSomething(params Expression>[] properties) { var query = // create LINQ query that returns IQueryable 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个属性的情况。

继Jay的答案之后 ,这可以成为一个很好的扩展方法:

 public static class EnumerableExtensions { public static IEnumerable OrderByMany(this IEnumerable enumerable, params Expression>[] 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 

UPDATE

您可以添加OrderByMany方法的另一个重载以支持SortOrder,尽管它很快就会变得笨拙。 就个人而言,我只是去寻找语法

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

但是,为了记录,至少在C#4中,我将使用枚举和元组完成重载。

 public enum SortOrder { Ascending, Descending } 

和额外的过载:

 public static IEnumerable OrderByMany(this IEnumerable enumerable, params Tuple>,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>, SortOrder>(x => x.Age, SortOrder.Descending), new Tuple>, SortOrder>(x => x.Name, SortOrder.Ascending))) { Console.WriteLine("{0} Age={1}", per.Name, per.Age); } 

要按任意属性排序,您需要构建一个表达式树以传递给OrderBy

要按任意数量的属性排序,您需要在循环中调用ThenBy

我喜欢Jamiec的想法,但我讨厌使用元组,因为语法很难看。 因此,我构建了一个封装了Tuple的小类,并为Item1和Item2属性公开了具有更好变量名的getter。

另请注意,我使用了升序的默认排序顺序,因此如果要按降序排序,则只需指定SortOrder。

 public class SortExpression { private Tuple>, SortOrder> tuple; public SortExpression( Expression> expression, SortOrder order =SortOrder.Ascending ) { tuple = new Tuple>, SortOrder>(expression, order); } public Expression> Expression { get { return tuple.Item1; } } public SortOrder Order { get { return tuple.Item2; } } } 

在我的特定应用程序中,我有一个存储库基类,它接受IQueryable并将其转换为ObservableCollection。 在该方法中,我使用SortExpression类:

 public ObservableCollection GetCollection(params SortExpression[] sortExpressions) { var list = new ObservableCollection(); 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(x => x.FirstName), new SortExpression(x => x.LastName));