2011-01-24 78 views
3

我需要在Linq(to Entity)語句中支持可變數量的Orderby條款。也就是說,我的函數將接受數據應該按順序排列的屬性列表。屬性可以有升序或降序排序。處理構建Linq查詢的最佳方式是什麼?Linq處理可變數量的OrderBy

謝謝!

回答

2

要按任意屬性排序,您需要構建一個表達式樹來傳遞給OrderBy

要按任意數量的屬性排序,您需要在循環中調用ThenBy

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種性能的情況下。

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 

UPDATE

您可以添加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); 
} 
+0

謝謝! 可以修改OrderBYMany來支持傳遞IComparer嗎? – JerryKur 2011-01-24 18:10:31

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