2011-09-01 111 views
57

如何使用我將其作爲參數的值指定傳遞給orderby的參數?如何動態指定Linq OrderBy參數?

例:

List<Student> existingStudends = new List<Student>{ new Student {...}, new Student {...}} 

目前執行:

List<Student> orderbyAddress = existingStudends.OrderBy(c => c.Address).ToList(); 

相反的c.Address,我怎麼可以把它看作一個參數?

string param = "City"; 
List<Student> orderbyAddress = existingStudends.OrderByDescending(c => param).ToList(); 
+2

你可能會尋找Dynamic Linq:http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx – BrokenGlass

+0

@Nev_Rahd:試圖澄清這個問題。另外,'OrderBy'是一個Linq特性,並且在'IEnumerable'上,而不是'List'特有的特性。隨意滾動編輯或進一步更改:) –

+0

[動態LINQ OrderBy在IEnumerable ](http:// stackoverflow。com/questions/41244/dynamic-linq-orderby-on-ienumerablet) –

回答

92

下面是使用反射成爲了可能......

var param = "Address";  
var propertyInfo = typeof(Student).GetProperty(param);  
var orderByAddress = items.OrderBy(x => propertyInfo.GetValue(x, null)); 
+0

這就是我正在尋找的。遺漏了原始答案中的.GetValue部分。 – tsells

+0

這是一個awsm代碼...謝謝你,它幫了我很多。 – Harsha

+2

但是,當涉及由供應商解釋的Linq表達式時,例如實體框架(sql server或其他)呢? –

1

這不會讓你通過一個string,當你問你的問題,但它仍然會爲你工作。

OrderByDescending方法採用Func<TSource, TKey>,所以你可以重寫你的函數是這樣的:

List<Student> QueryStudents<TKey>(Func<Student, TKey> orderBy) 
{ 
    return existingStudents.OrderByDescending(orderBy).ToList(); 
} 

還有其他重載OrderByDescending以及該採取Expression<Func<TSource, TKey>>,和/或IComparer<TKey>。你也可以看看這些,看看他們是否提供任何使用。

+0

這不起作用,因爲你沒有定義TKey的類型。您必須將您的更改爲。 –

+0

@PatrickDesjardins - 那更好? –

+0

這只是對我有用!我想要一個函數,可以根據傳遞的布爾值來升序或降序排列列表。你的代碼很好地調整了一下! –

67

您可以使用反射的一點點構建表達式樹如下(這是一個擴展方法):

public static IQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> source, string orderByProperty, 
          bool desc) 
{ 
    string command = desc ? "OrderByDescending" : "OrderBy"; 
    var type = typeof(TEntity); 
    var property = type.GetProperty(orderByProperty); 
    var parameter = Expression.Parameter(type, "p"); 
    var propertyAccess = Expression.MakeMemberAccess(parameter, property); 
    var orderByExpression = Expression.Lambda(propertyAccess, parameter); 
    var resultExpression = Expression.Call(typeof(Queryable), command, new Type[] { type, property.PropertyType }, 
            source.Expression, Expression.Quote(orderByExpression)); 
    return source.Provider.CreateQuery<TEntity>(resultExpression); 
} 

orderByProperty是你想,如果訂購的屬性名傳遞true作爲參數對於desc,將按降序排列;否則,將按升序排序。

現在,你應該能夠做到existingStudents.OrderBy("City",true);existingStudents.OrderBy("City",false);

+6

這個答案很棒,比反思的答案好得多。這實際上適用於其他提供者,如實體框架。 – Sam

+0

這太好了。謝謝。 – Saragis

+1

如果可以的話,我會投票給這十次!你在哪裏學習寫這樣的擴展方法?!! – Jach

4
private Func<T, object> GetOrderByExpression<T>(string sortColumn) 
    { 
     Func<T, object> orderByExpr = null; 
     if (!String.IsNullOrEmpty(sortColumn)) 
     { 
      Type sponsorResultType = typeof(T); 

      if (sponsorResultType.GetProperties().Any(prop => prop.Name == sortColumn)) 
      { 
       System.Reflection.PropertyInfo pinfo = sponsorResultType.GetProperty(sortColumn); 
       orderByExpr = (data => pinfo.GetValue(data, null)); 
      } 
     } 
     return orderByExpr; 
    } 

    public List<T> OrderByDir<T>(IEnumerable<T> source, string dir, Func<T, object> OrderByColumn) 
    { 
     return dir.ToUpper() == "ASC" ? source.OrderBy(OrderByColumn).ToList() : source.OrderByDescending(OrderByColumn).ToList();`` 
    } 

// Call the code like below 
     var orderByExpression= GetOrderByExpression<SearchResultsType>(sort); 

    var data = OrderByDir<SponsorSearchResults>(resultRecords, SortDirectionString, orderByExpression);  
+1

可能很好解釋這個? –

+0

輝煌!正是我需要的。 –

4

這裏的東西我想出了對付有條件降序。您可以將其與其他動態生成keySelector func的方法結合使用。

public static IOrderedQueryable<TSource> OrderBy<TSource, TKey>(this IQueryable<TSource> source, 
      System.Linq.Expressions.Expression<Func<TSource, TKey>> keySelector, 
      System.ComponentModel.ListSortDirection sortOrder 
      ) 
    { 
     if (sortOrder == System.ComponentModel.ListSortDirection.Ascending) 
      return source.OrderBy(keySelector); 
     else 
      return source.OrderByDescending(keySelector); 
    } 

用法:

//imagine this is some parameter 
var direction = System.ComponentModel.ListSortDirection.Ascending; 
query = query.OrderBy(ec => ec.MyColumnName, direction); 

注意這允許您鏈這個.OrderBy擴展了新的參數到任何IQueryable的。

// perhaps passed in as a request of user to change sort order 
// var direction = System.ComponentModel.ListSortDirection.Ascending; 
query = context.Orders 
     .Where(o => o.Status == OrderStatus.Paid) 
     .OrderBy(ec => ec.OrderPaidUtc, direction); 
1

是爲我工作在這裏https://gist.github.com/neoGeneva/1878868通過neoGeneva發佈的唯一解決方案。

我會重新發布他的代碼,因爲它運行良好,我不希望它在網頁中丟失!

public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string sortExpression) 
    { 
     if (source == null) 
      throw new ArgumentNullException("source", "source is null."); 

     if (string.IsNullOrEmpty(sortExpression)) 
      throw new ArgumentException("sortExpression is null or empty.", "sortExpression"); 

     var parts = sortExpression.Split(' '); 
     var isDescending = false; 
     var propertyName = ""; 
     var tType = typeof(T); 

     if (parts.Length > 0 && parts[0] != "") 
     { 
      propertyName = parts[0]; 

      if (parts.Length > 1) 
      { 
       isDescending = parts[1].ToLower().Contains("esc"); 
      } 

      PropertyInfo prop = tType.GetProperty(propertyName); 

      if (prop == null) 
      { 
       throw new ArgumentException(string.Format("No property '{0}' on type '{1}'", propertyName, tType.Name)); 
      } 

      var funcType = typeof(Func<,>) 
       .MakeGenericType(tType, prop.PropertyType); 

      var lambdaBuilder = typeof(Expression) 
       .GetMethods() 
       .First(x => x.Name == "Lambda" && x.ContainsGenericParameters && x.GetParameters().Length == 2) 
       .MakeGenericMethod(funcType); 

      var parameter = Expression.Parameter(tType); 
      var propExpress = Expression.Property(parameter, prop); 

      var sortLambda = lambdaBuilder 
       .Invoke(null, new object[] { propExpress, new ParameterExpression[] { parameter } }); 

      var sorter = typeof(Queryable) 
       .GetMethods() 
       .FirstOrDefault(x => x.Name == (isDescending ? "OrderByDescending" : "OrderBy") && x.GetParameters().Length == 2) 
       .MakeGenericMethod(new[] { tType, prop.PropertyType }); 

      return (IQueryable<T>)sorter 
       .Invoke(null, new object[] { source, sortLambda }); 
     } 

     return source; 
    } 
4

1)安裝System.Linq.Dynamic

2)添加以下代碼

public static class OrderUtils 
{ 
    public static string ToStringForOrdering<T, TKey>(this Expression<Func<T, TKey>> expression, bool isDesc = false) 
    { 
     var str = expression.Body.ToString(); 
     var param = expression.Parameters.First().Name; 
     str = str.Replace("Convert(", "(").Replace(param + ".", ""); 
     return str + (isDesc ? " descending" : ""); 
    } 
} 

3)撰寫您的開關lambda函數

public static class SortHelper 
{ 
    public static Expression<Func<UserApp, object>> UserApp(string orderProperty) 
    { 
     orderProperty = orderProperty?.ToLowerInvariant(); 
     switch (orderProperty) 
     { 
      case "firstname": 
       return x => x.PersonalInfo.FirstName; 
      case "lastname": 
       return x => x.PersonalInfo.LastName; 
      case "fullname": 
       return x => x.PersonalInfo.FirstName + x.PersonalInfo.LastName; 
      case "email": 
       return x => x.Email; 

     } 
    } 
} 

4的選擇)使用你的助手

Dbset.OrderBy(SortHelper.UserApp("firstname").ToStringForOrdering()) 

5)您可以用pagging(PagedList

public virtual IPagedList<T> GetPage<TOrder>(Page page, Expression<Func<T, bool>> where, Expression<Func<T, TOrder>> order, bool isDesc = false, 
     params Expression<Func<T, object>>[] includes) 
    { 
     var orderedQueryable = Dbset.OrderBy(order.ToStringForOrdering(isDesc)); 
     var query = orderedQueryable.Where(where).GetPage(page); 
     query = AppendIncludes(query, includes); 

     var results = query.ToList(); 
     var total = Dbset.Count(where); 

     return new StaticPagedList<T>(results, page.PageNumber, page.PageSize, total); 
    } 

說明使用

System.Linq.Dynamic使我們能夠在排序依據的方法設置字符串值。但是在這個擴展中,字符串將被解析爲Lambda。所以我認爲它會工作,如果我們將解析Lambda字符串並將其提供給OrderBy方法。它的工作原理!

0

我很晚參加聚會,但這些解決方案都不適合我。我急於嘗試System.Linq.Dynamic,但我無法在Nuget上找到它,也許折舊?無論哪種方式......

這是我想出的解決方案。我需要動態使用OrderBy,OrderByDescendingOrderBy> ThenBy

我簡單地爲我的列表對象創建了一個擴展方法,我知道有一點哈克...我不會推薦這個,如果這是我做了很多事情,但它對一個好的東西是好的。

List<Employee> Employees = GetAllEmployees(); 

foreach(Employee oEmployee in Employees.ApplyDynamicSort(eEmployeeSort)) 
{ 
    //do stuff 
} 

public static IOrderedEnumerable<Employee> ApplyDynamicSort(this List<Employee> lEmployees, Enums.EmployeeSort eEmployeeSort) 
{ 
    switch (eEmployeeSort) 
    { 
     case Enums.EmployeeSort.Name_ASC: 
      return lEmployees.OrderBy(x => x.Name); 
     case Enums.EmployeeSort.Name_DESC: 
      return lEmployees.OrderByDescending(x => x.Name); 
     case Enums.EmployeeSort.Department_ASC_Salary_DESC: 
      return lEmployees.OrderBy(x => x.Department).ThenByDescending(y => y.Salary); 
     default: 
      return lEmployees.OrderBy(x => x.Name); 
    } 
} 
0
  • 炸藥添加金塊包到您的代碼

  • 添加命名空間Dynamite.Extensions 例如:使用Dynamite.Extensions;

  • 通過查詢爲任意SQL查詢提供排序 例如:students.OrderBy(「City DESC,Address」)。ToList();

0

爲了擴大在answer by @Icarus:如果你想擴展方法的返回類型是一個IOrderedQueryable而不是一個IQueryable,你可以簡單地把結果如下:

public static IOrderedQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> source, string orderByProperty, bool desc) 
{ 
    string command = desc ? "OrderByDescending" : "OrderBy"; 
    var type = typeof(TEntity); 
    var property = type.GetProperty(orderByProperty); 
    var parameter = Expression.Parameter(type, "p"); 
    var propertyAccess = Expression.MakeMemberAccess(parameter, property); 
    var orderByExpression = Expression.Lambda(propertyAccess, parameter); 
    var resultExpression = Expression.Call(typeof(Queryable), command, new Type[] { type, property.PropertyType }, 
     source.Expression, Expression.Quote(orderByExpression)); 
    return (IOrderedQueryable<TEntity>)source.Provider.CreateQuery<TEntity>(resultExpression); 
}