如何动态指定Linq OrderBy参数?

Sre*_*har 82 c# linq

如何指定传递给orderby使用我作为参数的值的参数?

例如:

List<Student> existingStudends = new List<Student>{ new Student {...}, new Student {...}}
Run Code Online (Sandbox Code Playgroud)

目前实施:

List<Student> orderbyAddress = existingStudends.OrderBy(c => c.Address).ToList();
Run Code Online (Sandbox Code Playgroud)

而不是c.Address,我如何将其作为参数?

 string param = "City";
 List<Student> orderbyAddress = existingStudends.OrderByDescending(c => param).ToList();
Run Code Online (Sandbox Code Playgroud)

cod*_*ion 119

这是使用反射的可能性......

var param = "Address";    
var propertyInfo = typeof(Student).GetProperty(param);    
var orderByAddress = items.OrderBy(x => propertyInfo.GetValue(x, null));
Run Code Online (Sandbox Code Playgroud)

  • 当我尝试这个时,我得到错误:LINQ to Entities无法识别方法'System.Object GetValue(System.Object,System.Object [])'方法,并且此方法无法转换为商店表达式.这个答案是否只适用于Linq To SQL? (5认同)
  • .AsEnumerable()没有错误:var orderByAddress = items.AsEnumerable().OrderBy(x => propertyInfo.GetValue(x,null)); (4认同)
  • 但是当涉及由提供者解释的Linq表达式时,如实体框架(sql server,还是其他)? (3认同)
  • @vijay - 使用[`ThenBy`方法](http://msdn.microsoft.com/en-us/library/system.linq.queryable.thenby.aspx). (2认同)

Ica*_*rus 111

您可以使用一点反射来构造表达式树,如下所示(这是一个扩展方法):

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);
}
Run Code Online (Sandbox Code Playgroud)

orderByProperty是您要订购的属性名称,如果传递为true作为参数desc,将按降序排序; 否则,将按升序排序.

现在你应该可以做existingStudents.OrderBy("City",true);existingStudents.OrderBy("City",false);

  • 这个答案很棒,比反思答案要好得多.这实际上适用于实体框架等其他提供商. (9认同)
  • 使用 EFCore 3.0 时,这似乎不再起作用,我收到运行时错误,无法转换查询。 (4认同)
  • 它应该返回IOrderedQueryable,就像内置的OrderBy一样吗?那样的话,你可以打电话给他. (3认同)
  • 是的,@Mildan,这对我来说也适用于 3.0 和 3.1。出现错误〜“无法翻译”。如果相关的话,我将 Pomelo 用于 MySQl。问题在于表达式。如果你手动编写表达式,它就可以工作。因此,不要提供 Lambda.Expression(),只需提供类似以下内容: LambdaExpression orderByExp1 = (Expression&lt;Func&lt;AgencySystemBiz, string&gt;&gt;)(x =&gt; x.Name); (3认同)
  • 如果可以的话,我会投票十次!你在哪里学会写这样的扩展方法?? !! (2认同)
  • 是否可以添加任何巫毒,以使实体继承自的类的属性能够正常工作?我通常有一个我的所有db类都继承自该类的类,它具有诸如`createddate`等简单的内容,但是不幸的是,如果排序字符串/属性来自基类,则该类不起作用。 (2认同)
  • 哈,如果我有子类并想按属性排序该怎么办: List&lt;Student&gt; orderbyAddress = existingStudends.OrderBy(c =&gt; c.Address).ToList(); ? (2认同)

小智 7

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" : "");
    }
}
Run Code Online (Sandbox Code Playgroud)

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;

        }
    }
}
Run Code Online (Sandbox Code Playgroud)

4)使用你的助手

Dbset.OrderBy(SortHelper.UserApp("firstname").ToStringForOrdering())
Run Code Online (Sandbox Code Playgroud)

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);
    }
Run Code Online (Sandbox Code Playgroud)

说明

System.Linq.Dynamic允许我们在OrderBy方法中设置字符串值.但是在这个扩展中,字符串将被解析为Lambda.所以我认为如果我们将Lambda解析为字符串并将其提供给OrderBy方法将会有效.它的工作原理!


use*_*420 6

   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);    
Run Code Online (Sandbox Code Playgroud)


Cia*_*ran 6

通过@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);
}
Run Code Online (Sandbox Code Playgroud)

  • 其他答案似乎不适用于实体框架。这是 EF 的完美解决方案,因为 Linq to Entities 不支持 GetProperty、GetValue (2认同)

Aar*_*nLS 5

这是我为处理条件降序而提出的一些方法。您可以将其与其他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);
    }
Run Code Online (Sandbox Code Playgroud)

用法:

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

请注意,这允许您将此.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);
Run Code Online (Sandbox Code Playgroud)