2011-09-01 18 views
57

Làm cách nào để chỉ định đối số được chuyển đến orderby bằng cách sử dụng giá trị mà tôi lấy làm tham số?Làm cách nào để chỉ định đối số LINQ OrderBy động?

Ex:

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

Hiện nay thực hiện:

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

Thay vì c.Address, làm thế nào tôi có thể coi đó là một tham số?

Ví dụ

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

Bạn có thể tìm kiếm LINQ động: http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx – BrokenGlass

+0

@Nev_Rahd: Đã cố gắng làm rõ câu hỏi một chút. Ngoài ra, 'OrderBy' là một tính năng LINQ, và là trên' IEnumerable', không phải là một tính năng cụ thể cho 'List'. Vui lòng cuộn lại bản chỉnh sửa hoặc thay đổi thêm :) –

+0

Bản sao có thể có của [Thứ tự LINQ động trên IEnumerable ] (http: // stackoverflow.com/questions/41244/dynamic-linq-orderby-on-ienumerablet) –

Trả lời

92

Dưới đây là một possiblity sử dụng phản ánh ...

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

Đây là những gì tôi đang tìm kiếm. Đã thiếu phần .GetValue từ câu trả lời ban đầu của tôi. – tsells

+0

đây là một mã awsm ... cảm ơn bạn, nó đã giúp tôi rất nhiều. – Harsha

+2

Nhưng có đúng khi nói đến các biểu thức LINQ được giải thích bởi các nhà cung cấp, như Entity Framework (máy chủ sql hay khác) ?? –

1

này không cho phép bạn vượt qua một string, như bạn yêu cầu trong câu hỏi của bạn, nhưng nó vẫn có thể làm việc cho bạn.

Phương pháp OrderByDescending mất một Func<TSource, TKey>, vì vậy bạn có thể viết lại chức năng của bạn theo cách này:

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

Có quá tải khác cho OrderByDescending cũng mà phải mất một Expression<Func<TSource, TKey>>, và/hoặc một IComparer<TKey>. Bạn cũng có thể xem xét chúng và xem liệu chúng có cung cấp cho bạn bất kỳ thứ gì sử dụng hay không.

+0

Điều này không hoạt động bởi vì bạn không xác định loại TKey. Thay vào đó, bạn phải thay đổi để có . –

+0

@PatrickDesjardins - tốt hơn? –

+0

Đây chỉ là những gì làm việc cho tôi! Tôi muốn một hàm có thể sắp xếp một danh sách tăng dần hoặc giảm dần, tùy thuộc vào giá trị bool đã truyền. Mã của bạn hoạt động tốt với một chút tinh chỉnh! –

67

Bạn có thể sử dụng một chút suy nghĩ để xây dựng cây biểu hiện như sau (đây là một phương pháp mở rộng):

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 là tên tài sản bạn muốn đặt hàng bằng và nếu vượt qua đúng như thông số cho desc, sẽ sắp xếp theo thứ tự giảm dần; nếu không, sẽ sắp xếp theo thứ tự tăng dần.

Bây giờ bạn sẽ có thể làm existingStudents.OrderBy("City",true); hoặc existingStudents.OrderBy("City",false);

+6

Câu trả lời này tuyệt vời và tốt hơn nhiều so với câu trả lời phản chiếu. Điều này thực sự làm việc với các nhà cung cấp khác như khung thực thể. – Sam

+0

Điều này thật tuyệt. Cảm ơn. – Saragis

+1

Tôi sẽ bỏ phiếu mười lần này nếu tôi có thể !!! Nơi nào bạn học cách viết phương pháp mở rộng như thế này ?? !! – 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

Có thể giải thích điều này? –

+0

Brilliant! Chính xác những gì tôi cần. –

4

Đây là điều mà tôi đã đưa ra để đối phó với một giảm dần có điều kiện. Bạn có thể kết hợp điều này với các phương thức khác để tạo ra keySelector func động.

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

Cách sử dụng:

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

Thông báo này cho phép bạn chuỗi .OrderBy phần mở rộng này với một tham số mới vào bất kỳ 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

Giải pháp duy nhất làm việc cho tôi đã được đăng ở đây https://gist.github.com/neoGeneva/1878868 bởi neoGeneva.

Tôi sẽ đăng lại mã của anh ấy vì nó hoạt động tốt và tôi sẽ không muốn nó bị mất trong mạng nội bộ!

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) Cài đặt System.Linq.Dynamic

2) Thêm đoạn mã sau

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) Viết chuyển đổi của bạn để lựa chọn của Lambda chức năng

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) Sử dụng người giúp đỡ của bạn

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

5) Bạn có thể sử dụng nó với 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); 
    } 

Giải thích

System.Linq.Dynamic cho phép chúng ta thiết lập giá trị chuỗi trong phương pháp OrderBy. Nhưng bên trong phần mở rộng này, chuỗi sẽ được phân tích cú pháp thành Lambda. Vì vậy, tôi nghĩ rằng nó sẽ làm việc nếu chúng ta sẽ phân tích Lambda thành chuỗi và đưa nó vào phương thức OrderBy. Và nó hoạt động!

0

Tôi đến trễ với bữa tiệc nhưng không có giải pháp nào trong số này phù hợp với tôi. Tôi đã háo hức thử System.Linq.Dynamic, nhưng tôi không thể tìm thấy điều đó trên Nuget, có thể bị mất giá? Dù bằng cách nào ...

Đây là giải pháp mà tôi đã đưa ra. Tôi cần phải sử dụng động một hỗn hợp của Đơn hàngBởi, Đơn hàngBằng số tăng dầnOrderBy> ThenBy.

Tôi chỉ cần tạo một phương pháp mở rộng cho đối tượng danh sách của mình, một chút hacky Tôi biết ... Tôi sẽ không đề xuất điều này nếu nó là thứ tôi đang làm, nhưng rất tốt cho một lần tắt.

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
  • Thêm gói nugget Dynamite mã của bạn

  • Thêm namespace Dynamite.Extensions Ví dụ: sử dụng Dynamite.Extensions;

  • Đặt hàng theo truy vấn giống như bất kỳ truy vấn SQL nào Ví dụ: students.OrderBy ("City DESC, Address"). ToList();

0

Mở rộng trên answer by @Icarus: nếu bạn muốn kiểu trả về của phương pháp mở rộng là một IOrderedQueryable thay vì một IQueryable, bạn chỉ có thể đúc kết quả như sau:

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); 
} 
Các vấn đề liên quan