2008-11-21 3 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); 
} 

Поскольку тип для OrderBy не вытекает из sortExpression я должен указать это что-то вроде этого во время выполнения:Как применить OrderBy в IQueryable, используя имя столбца строки в универсальном методе расширения?

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

Или

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

Я не думаю, что это возможно однако, поскольку TSortColumn может быть определен только во время выполнения.

Есть ли способ обойти это?

+0

Не уверен, что если [это] (http://stackoverflow.com/a/12920204/1139347), что вы ищете, но посмотрите. Cheers – joaopintocruz 2012-10-16 17:33:35

+0

@JTew Как я могу реализовать второй порядок по пункту..показать orderby id, а затем по дате – SRJ 2014-07-03 13:10:23

ответ

100

Мы сделали что-то подобное (не 100% то же самое, но подобное) в LINQ для SQL проекта. Вот код:

public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string ordering, params object[] values) { 
    var type = typeof(T); 
    var property = type.GetProperty(ordering); 
    var parameter = Expression.Parameter(type, "p"); 
    var propertyAccess = Expression.MakeMemberAccess(parameter, property); 
    var orderByExp = Expression.Lambda(propertyAccess, parameter); 
    MethodCallExpression resultExp = Expression.Call(typeof(Queryable), "OrderBy", new Type[] { type, property.PropertyType }, source.Expression, Expression.Quote(orderByExp)); 
    return source.Provider.CreateQuery<T>(resultExp); 
} 

Мы фактически не использовать общий, у нас был известный класс, но он должен работать на общий (я поставил общий заполнитель, где он должен быть).

Edit: Для убывающего порядка, проходит в OrderByDescending вместо «OrderBy»:

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

Хех, нет, я никак не могу назначить ответ себе :) – JTew 2008-11-21 03:47:36

+1

по убыванию, перейдите в «OrderByDescending» вместо «OrderBy» Результат методаCallExpressionExp = Expression.Call (typeof (Queryable) », OrderByDescending ", ... – 2011-07-26 19:18:44

6

Кажется, что this является способ сделать это, теперь, чтобы проверить, что:

// ***** 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 секунды позади! : P – 2008-11-21 02:01:51

10

я расширил свои функции, чтобы добавить поддержку свойств ребенка.

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

Вы можете использовать эти функции, как:

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

Вы также можете использовать динамические Linq

Информация здесь http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx

C# скачать здесь http://msdn.microsoft.com/en-us/vcsharp/bb894665.aspx

Затем просто добавьте использование Linq.Dynamic; и вы автоматически получаете 2 дополнительных метода расширения, которые можно использовать как это

return query.OrderBy("StringColumnName"); 
8

Я использовал вашу идею для метода расширения для OrderBy. Но в случае «многих-многих» я получаю ошибку. Например, у вас есть таблица Site, Customer и Customer_site. Для данного сайта я хочу сортировать по имени клиента и в расширении OrderBy (когда я прохожу «site.customer», где клиент является навигация свойство) Я получаю сообщение об ошибке в строке: propertyAccess = Expression.MakeMemberAccess (propertyAccess, недвижимость);

Это то, что я использую (с некоторыми улучшениями :-)):

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

С уважением

Слободан

1

Если вы можете добавить «System.Linq.Динамический»пакет затем, слишком легко, без каких-либо осложнений,

Fisrt insatll пакет "System.Linq.Dynamic" из менеджера пакетов NuGet затем попробовать, как показано ниже, как ваша потребность,

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

Надеется, что это поможет

0

Я установил этот код немного: https://stackoverflow.com/a/1670085/5852630

Этот код работает с последовательной сортировкой: (Нет "OrderBy") первым выполнить "OrderBy", затем "ThenBy"

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