IEnumerable<T> / IQueryable<T> 上的动态LINQ OrderBy

749

我在VS2008的示例中找到了一个关于动态LINQ的例子,允许你使用类似SQL语句的字符串(例如OrderBy("Name, Age DESC"))进行排序。不幸的是,该方法只适用于IQueryable<T>。是否有办法在IEnumerable<T>上实现这个功能呢?

24个回答

5
你可以添加它:
public static IEnumerable<T> OrderBy( this IEnumerable<T> input, string queryString) {
    //parse the string into property names
    //Use reflection to get and sort by properties
    //something like

    foreach( string propname in queryString.Split(','))
        input.OrderBy( x => GetPropertyValue( x, propname ) );

    // I used Kjetil Watnedal's reflection example
}
< p > GetPropertyValue 函数来自 Kjetil Watnedal 的回答

问题是为什么?任何这样的排序都会在运行时抛出异常,而不是编译时(比如 D2VIANT 的答案)。

如果你正在处理 Linq to Sql 并且 orderby 是表达式树,它将被转换为 SQL 以进行执行。


GetPropertyValue 方法将被执行于所有元素,这是一个不好的解决方案。 - Alex Shkor
2
OrderBy 不会保持之前的顺序!! - Amir Ismail

4
您可以使用以下内容:
        public List<Book> Books(string orderField, bool desc, int skip, int take)
{
    var propertyInfo = typeof(Book).GetProperty(orderField);

    return _context.Books
        .Where(...)
        .OrderBy(p => !desc ? propertyInfo.GetValue(p, null) : 0)
        .ThenByDescending(p => desc ? propertyInfo.GetValue(p, null) : 0)
        .Skip(skip)
        .Take(take)
        .ToList();
}

几年后我偶然发现了这个;这对我来说像梦一样有效。我有1到3个属性的动态排序,这个方法非常好用。易于实现且无需麻烦。 - Bazïnga
喜欢这个回答,但如果我需要按子类的属性进行排序,该怎么做呢? - RoLYroLLs

4

我找到了一些有趣的东西。 如果您的数据源是DataTable,您可以使用动态排序而不必使用动态Linq。

DataTable orders = dataSet.Tables["SalesOrderHeader"];
EnumerableRowCollection<DataRow> query = from order in orders.AsEnumerable()
                                         orderby order.Field<DateTime>("OrderDate")
                                         select order;
DataView view = query.AsDataView();
bindingSource1.DataSource = view;

参考:http://msdn.microsoft.com/en-us/library/bb669083.aspx(使用 DataSetExtensions)

这里还有一种方法,通过将其转换为 DataView 来实现:

DataTable contacts = dataSet.Tables["Contact"];    
DataView view = contacts.AsDataView();    
view.Sort = "LastName desc, FirstName asc";    
bindingSource1.DataSource = view;
dataGridView1.AutoResizeColumns();

4

您可以将IEnumerable转换为IQueryable。

items = items.AsQueryable().OrderBy("Name ASC");

3
这篇回答是对需要一个示例来解决@John Sheehan - Runscope提供的解决方案的评论做出的回应。
请为我们其他人提供一个示例。
在DAL(数据访问层)中,
IEnumerable版本:
public  IEnumerable<Order> GetOrders()
{
    // i use Dapper to return IEnumerable<T> using Query<T>
    //.. do stuff

    return orders  // IEnumerable<Order>
}

IQueryable版本

public IQueryable<Order> GetOrdersAsQuerable()
{
    IEnumerable<Order> qry= GetOrders();

    // use the built-in extension method  AsQueryable in  System.Linq namespace
    return qry.AsQueryable();            
}

现在,您可以使用IQueryable版本来绑定例如Asp.net中的GridView,并受益于排序(您无法使用IEnumerable版本进行排序)。

我使用Dapper作为ORM,构建了IQueryable版本,并在Asp.net中的GridView中利用排序非常容易。


3
你可以这样定义一个从字符串到 Func<> 的字典:
Dictionary<string, Func<Item, object>> SortParameters = new Dictionary<string, Func<Item, object>>()
{
    {"Rank", x => x.Rank}
};

并且像这样使用:

yourList.OrderBy(SortParameters["Rank"]);

在这种情况下,您可以动态按字符串进行排序。

1
不错!对于那些无法使用动态 Linq 库的人来说,这是一个非常简单的选择。 - Ash8087
1
你的!这很简单,对我很有效。 - A3IOU

3

另一种解决方案使用以下类/接口。它不是真正的动态,但它可以工作。

public interface IID
{
    int ID
    {
        get; set;
    }
}

public static class Utils
{
    public static int GetID<T>(ObjectQuery<T> items) where T:EntityObject, IID
    {
        if (items.Count() == 0) return 1;
        return items.OrderByDescending(u => u.ID).FirstOrDefault().ID + 1;
    }
}

3

多亏了Maarten (在LINQ中使用PropertyInfo对象查询集合),我得到了这个解决方案:

myList.OrderByDescending(x => myPropertyInfo.GetValue(x, null)).ToList();

在我的情况下,我正在处理一个“ColumnHeaderMouseClick”(WindowsForm)事件,所以只需找到特定的列并找到它对应的PropertyInfo:
foreach (PropertyInfo column in (new Process()).GetType().GetProperties())
{
    if (column.Name == dgvProcessList.Columns[e.ColumnIndex].Name)
    {}
}

或者

PropertyInfo column = (new Process()).GetType().GetProperties().Where(x => x.Name == dgvProcessList.Columns[e.ColumnIndex].Name).First();

请确保您的列名与对象属性匹配

干杯


1
你可以像这样使用多个 order by 进行操作。
IOrderedEnumerable<JToken> sort;

if (query.OrderBys[0].IsDESC)
{
    sort = jarry.OrderByDescending(r => (string)r[query.OrderBys[0].Key]);
}
else
{
    sort = jarry.OrderBy(r =>
        (string) r[query.OrderBys[0].Key]); 
}

foreach (var item in query.OrderBys.Skip(1))
{
    if (item.IsDESC)
    {
        sort = sort.ThenByDescending(r => (string)r[item.Key]);
    }
    else
    {
        sort = sort.ThenBy(r => (string)r[item.Key]);
    }
}

0

将List转换为IEnumerable或Iquerable,添加using System.LINQ.Dynamic命名空间,然后您可以在逗号分隔的字符串中提及属性名称以OrderBy方法,默认来自System.LINQ.Dynamic。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接