2008-11-21 10 views
75
public static IQueryable<TResult> ApplySortFilter<T, TResult>(this IQueryable<T> query, string columnName) 
    where T : EntityObject 
{ 
    var param = Expression.Parameter(typeof(T), "o"); 
    var body = Expression.PropertyOrField(param,columnName); 

    var sortExpression = Expression.Lambda(body, param); 
    return query.OrderBy(sortExpression); 
} 

Bởi vì kiểu cho OrderBy không suy ra từ sortExpression tôi cần phải xác định nó một cái gì đó như thế này tại thời gian chạy:Làm cách nào để áp dụng OrderBy trên IQueryable bằng cách sử dụng tên cột chuỗi trong một phương pháp mở rộng chung?

var sortExpression = Expression.Lambda<T, TSortColumn>(body, param); 

Hoặc

return query.OrderBy<T, TSortColumn>(sortExpression); 

Tôi không nghĩ rằng đây có thể tuy nhiên TSortColumn chỉ có thể được xác định trong thời gian chạy.

Có cách nào khác không?

+0

Không chắc chắn nếu [this] (http://stackoverflow.com/a/12920204/1139347) những gì bạn đang tìm kiếm, nhưng hãy xem. Chúc mừng – joaopintocruz

+0

@JTew Làm thế nào tôi có thể thực hiện một đơn đặt hàng thứ hai theo mệnh đề ... id đặt hàng sau đó theo ngày – SRJ

Trả lời

100

Chúng tôi đã làm một cái gì đó tương tự (không phải 100% như nhau, nhưng tương tự) trong một dự án LINQ to SQL. Đây là mã:

Chúng tôi không thực sự sử dụng chung, chúng tôi có một lớp đã biết, nhưng nó nên làm việc chung (tôi đã đặt trình giữ chỗ chung nơi cần).

Edit: Đối với thứ tự giảm dần, vượt qua trong OrderByDescending thay vì "OrderBy":

MethodCallExpression resultExp = Expression.Call(typeof(Queryable), "OrderByDescending", new Type[] { type, property.PropertyType }, source.Expression, Expression.Quote(orderByExp)); 
+0

Heh no prob, Tôi không thể gán câu trả lời cho chính mình anyways :) – JTew

+1

cho thứ tự giảm dần, vượt qua trong "OrderByDescending" thay vì "OrderBy" MethodCallExpression resultExp = Expression.Call (typeof (Queryable), "OrderByDescending ", ... –

+3

Điều này làm việc tốt, nhưng sau đây chỉ là một ví dụ mã sạch thực sự tốt đẹp: http://stackoverflow.com/questions/41244/dynamic-linq-orderby – BenSwayne

6

Dường như this là cách để làm điều đó, bây giờ để xác minh rằng:

// ***** OrderBy(company => company) ***** 
// Create an expression tree that represents the expression 
// 'whereCallExpression.OrderBy(company => company)' 
MethodCallExpression orderByCallExpression = Expression.Call(
    typeof(Queryable), 
    "OrderBy", 
    new Type[] { queryableData.ElementType, queryableData.ElementType }, 
    whereCallExpression, 
    Expression.Lambda<Func<string, string>>(pe, new ParameterExpression[] { pe })); 
// ***** End OrderBy ***** 
+1

damnit, 34 giây sau! : P –

10

tôi đã mở rộng chức năng của mình để hỗ trợ thêm cho tính trẻ em.

private static LambdaExpression GenerateSelector<TEntity>(String propertyName, out Type resultType) where TEntity : class 
{ 
    // Create a parameter to pass into the Lambda expression (Entity => Entity.OrderByField). 
    var parameter = Expression.Parameter(typeof(TEntity), "Entity"); 
    // create the selector part, but support child properties 
    PropertyInfo property; 
    Expression propertyAccess; 
    if (propertyName.Contains('.')) 
    { 
      // support to be sorted on child fields. 
      String[] childProperties = propertyName.Split('.'); 
      property = typeof(TEntity).GetProperty(childProperties[0]); 
      propertyAccess = Expression.MakeMemberAccess(parameter, property); 
      for (int i = 1; i < childProperties.Length; i++) 
      { 
        property = property.PropertyType.GetProperty(childProperties[i]); 
        propertyAccess = Expression.MakeMemberAccess(propertyAccess, property); 
      } 
    } 
    else 
    { 
      property = typeof(TEntity).GetProperty(propertyName); 
      propertyAccess = Expression.MakeMemberAccess(parameter, property); 
    } 
    resultType = property.PropertyType;      
    // Create the order by expression. 
    return Expression.Lambda(propertyAccess, parameter); 
} 

private static MethodCallExpression GenerateMethodCall<TEntity>(IQueryable<TEntity> source, string methodName, String fieldName) where TEntity : class 
{ 
    Type type = typeof(TEntity); 
    Type selectorResultType; 
    LambdaExpression selector = GenerateSelector<TEntity>(fieldName, out selectorResultType); 
    MethodCallExpression resultExp = Expression.Call(typeof(Queryable), methodName, 
            new Type[] { type, selectorResultType }, 
            source.Expression, Expression.Quote(selector)); 
    return resultExp; 
} 

Bạn có thể sử dụng các chức năng như:

GenerateMethodCall<TEntity>(source, "OrderByDescending", fieldName); 
+1

Bạn là anh hùng của tôi !! –

+0

gotta yêu người thông minh –

27

Bạn cũng có thể sử dụng động LINQ

Thông tin ở đây http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx

C# download tại đây http://msdn.microsoft.com/en-us/vcsharp/bb894665.aspx

Sau đó, chỉ cần thêm Linq.Dynamic sử dụng; và bạn tự động nhận được 2 phương pháp tiện ích mở rộng bổ sung có thể được sử dụng như thế này

return query.OrderBy("StringColumnName"); 
+0

Cảm ơn, tôi đã nhìn thấy Linq.Dynamic về trong một mẫu tại trang web Phil Haack nhưng không chắc chắn về nó. Tôi sẽ có một vở kịch với điều này vào cuối tuần. – JTew

+0

Có thể tải xuống Systems.Linq.Dynamic.dll từ đây: https://github.com/kahanu/System.Linq.Dynamic – Baig

8

Tôi đã sử dụng ý tưởng của bạn cho phương pháp mở rộng cho OrderBy. Nhưng trong trường hợp "nhiều đến nhiều" tôi nhận được lỗi. Ví dụ bạn có bảng Site, Customer và Customer_site. Đối với trang web nhất định, tôi muốn sắp xếp theo tên khách hàng và trong phần mở rộng OrderBy (khi tôi chuyển "site.customer" nơi khách hàng là tài sản điều hướng), tôi gặp lỗi trong dòng: propertyAccess = Expression.MakeMemberAccess (propertyAccess, property);

Đây là những gì tôi sử dụng (với một số cải tiến :-)):

public static IQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> source, string orderByValues) where TEntity : class 
{ 
    IQueryable<TEntity> returnValue = null; 

    string orderPair = orderByValues.Trim().Split(',')[0]; 
    string command = orderPair.ToUpper().Contains("DESC") ? "OrderByDescending" : "OrderBy"; 

    var type = typeof(TEntity); 
    var parameter = Expression.Parameter(type, "p"); 

    string propertyName = (orderPair.Split(' ')[0]).Trim(); 

    System.Reflection.PropertyInfo property; 
    MemberExpression propertyAccess; 

    if (propertyName.Contains('.')) 
    { 
    // support to be sorted on child fields. 
    String[] childProperties = propertyName.Split('.'); 
    property = typeof(TEntity).GetProperty(childProperties[0]); 
    propertyAccess = Expression.MakeMemberAccess(parameter, property); 

    for (int i = 1; i < childProperties.Length; i++) 
    { 
     Type t = property.PropertyType; 
     if (!t.IsGenericType) 
     { 
     property = t.GetProperty(childProperties[i]); 
     } 
     else 
     { 
     property = t.GetGenericArguments().First().GetProperty(childProperties[i]); 
     } 

     propertyAccess = Expression.MakeMemberAccess(propertyAccess, property); 
    } 
    } 
    else 
    { 
    property = type.GetProperty(propertyName); 
    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)); 

    returnValue = source.Provider.CreateQuery<TEntity>(resultExpression); 

    if (orderByValues.Trim().Split(',').Count() > 1) 
    { 
    // remove first item 
    string newSearchForWords = orderByValues.ToString().Remove(0, orderByValues.ToString().IndexOf(',') + 1); 
    return source.OrderBy(newSearchForWords); 
    } 

    return returnValue; 
} 

Trân

Slobodan

1

Nếu bạn có thể thêm "System.Linq.Năng động" gói sau đó, Quá dễ dàng mà không cần bất kỳ biến chứng,

fisrt insatll gói "System.Linq.Dynamic" từ quản lý gói NuGet sau đó thử như sau như nhu cầu của bạn,

Ex:

public IQueryable<TEntity> GetWithInclude(Expression<Func<TEntity, bool>> predicate, 
        List<string> sortBy, int pageNo, int pageSize = 12, params string[] include) 
     { 
      try 
      { 
       var numberOfRecordsToSkip = pageNo * pageSize; 
       var dynamic = DbSet.AsQueryable(); 

       foreach (var s in include) 
       { 
        dynamic.Include(s); 
       } 
       return dynamic.OrderBy("CreatedDate").Skip(numberOfRecordsToSkip).Take(pageSize); 


      } 
      catch (Exception e) 
      { 
       throw new Exception(e.Message); 
      } 
     } 

Hy vọng điều này sẽ giúp

0

tôi cố định mã này một chút: https://stackoverflow.com/a/1670085/5852630

Mã này hoạt động với sắp xếp tuần tự: đầu tiên thực hiện "OrderBy", sau đó "ThenBy" (Không phải "OrderBy"!)

public static IQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> source, string orderByValues) where TEntity : class 
{ 
    IQueryable<TEntity> returnValue = null; 

    string[] orderPairs = orderByValues.Trim().Split(','); 

    Expression resultExpression = source.Expression; 

    string strAsc = "OrderBy"; 
    string strDesc = "OrderByDescending"; 

    foreach (string orderPair in orderPairs) 
    { 
     if (string.IsNullOrWhiteSpace(orderPair)) 
      continue; 

     string[] orderPairArr = orderPair.Trim().Split(' '); 

     string propertyName = orderPairArr[0].Trim(); 
     string orderNarrow = orderPairArr.Length > 1 ? orderPairArr[1].Trim() : string.Empty; 

     string command = orderNarrow.ToUpper().Contains("DESC") ? strDesc : strAsc; 

     Type type = typeof(TEntity); 
     ParameterExpression parameter = Expression.Parameter(type, "p"); 

     System.Reflection.PropertyInfo property; 
     Expression propertyAccess; 

     if (propertyName.Contains('.')) 
     { 
      // support to be sorted on child fields. 
      String[] childProperties = propertyName.Split('.'); 
      property = typeof(TEntity).GetProperty(childProperties[0]); 
      propertyAccess = Expression.MakeMemberAccess(parameter, property); 

      for (int i = 1; i < childProperties.Length; i++) 
      { 
       Type t = property.PropertyType; 
       if (!t.IsGenericType) 
       { 
        property = t.GetProperty(childProperties[i]); 
       } 
       else 
       { 
        property = t.GetGenericArguments().First().GetProperty(childProperties[i]); 
       } 

       propertyAccess = Expression.MakeMemberAccess(propertyAccess, property); 
      } 
     } 
     else 
     { 
      property = type.GetProperty(propertyName); 
      propertyAccess = Expression.MakeMemberAccess(parameter, property); 
     } 

     if (property.PropertyType == typeof(object)) 
     { 
      propertyAccess = Expression.Call(propertyAccess, "ToString", null); 
     } 

     LambdaExpression orderByExpression = Expression.Lambda(propertyAccess, parameter); 

     resultExpression = Expression.Call(typeof(Queryable), command, new Type[] { type, property.PropertyType == typeof(object) ? typeof(string) : property.PropertyType }, 
      resultExpression, Expression.Quote(orderByExpression)); 

     strAsc = "ThenBy"; 
     strDesc = "ThenByDescending"; 
    } 

    returnValue = source.Provider.CreateQuery<TEntity>(resultExpression); 

    return returnValue; 
}