我需要在Linq(to Entity)语句中支持可变数量的Orderby术语。也就是说,我的函数将接受数据应该在其上排序的属性列表。属性可以具有升序或降序排序。处理构造Linq查询的最佳方法是什么?
谢谢!
答案 0 :(得分:9)
你应该能够沿着这些方向做点什么:
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个属性的情况。
答案 1 :(得分:3)
从Jay's answer开始,这可以成为一个很好的扩展方法:
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中,我会使用枚举和放大器完成重载。元组。
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 :(得分:2)
要按任意属性排序,您需要构建一个表达式树以传递给OrderBy
。
要按任意数量的属性排序,您需要在循环中调用ThenBy
。
答案 3 :(得分:0)
我喜欢Jamiec的想法,但我讨厌使用元组因为语法很难看。因此,我构建了一个封装了Tuple的小类,并为Item1和Item2属性公开了具有更好变量名的getter。
另请注意,我使用了升序的默认排序顺序,因此如果要按降序排序,则只需指定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));