0

我已经为我的 EF 通用存储库创建了一个 orderby 表达式,如下字符串 command = orderByDesc ?"OrderByDescending" : "OrderBy";

var type = typeof(T);

var property = type.GetProperty(orderby);

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

                       items.Expression, Expression.Quote(orderByExpression));
items = items.Provider.CreateQuery<T>(resultExpression);

现在我想创建带有 2 列用于排序的表达式,但找不到有用的东西。

请帮我创建一个包含 2 列的 orderby 表达式。

4

3 回答 3

6

我不明白接受的答案是如何被接受的答案,因为 OP 正在询问如何为多列排序场景创建表达式树

有时您必须使用表达式树手动构建OrderBy语句,因为您不知道用户想要对哪些列进行排序。例如,当您有一个使用Datatables构建的网格并且某些列是可排序的时,用户可以 SHIFT 单击列标题以按多列排序:

在此处输入图像描述

屏幕截图显示用户希望按 Cassette(即 a string)和 Slot Number(即 a double)对网格进行排序。

OrderBy* / ThenBy*

构建用于排序的表达式树时,棘手的部分是第一次需要使用OrderBy*,但第二次及以后需要切换到使用ThenBy*

我将演示如何使用扩展方法来做到这一点IQueryable

namespace DataTables.AspNet.Core
{
    public interface ISort
    {
        int Order { get; }
        SortDirection Direction { get; }
    }

    public enum SortDirection
    {
        Ascending = 0,
        Descending = 1
    }
}

namespace DL.SO.Framework.Mvc.DataTables.Extensions
{
    public static class QueryableExtensions
    {
        public static IQueryable<TModel> OrderByColumns<TModel>(
            this IQueryable<TModel> collection, 
            IDictionary<string, ISort> sortedColumns)
        {
            // Basically sortedColumns contains the columns user wants to sort by, and 
            // the sorting direction.
            // For my screenshot, the sortedColumns looks like
            // [
            //     { "cassette", { Order = 1, Direction = SortDirection.Ascending } },
            //     { "slotNumber", { Order = 2, Direction = SortDirection.Ascending } }
            // ]

            bool firstTime = true;

            // The type that represents each row in the table
            var itemType = typeof(TModel);

            // Name the parameter passed into the lamda "x", of the type TModel
            var parameter = Expression.Parameter(itemType, "x");

            // Loop through the sorted columns to build the expression tree
            foreach (var sortedColumn in sortedColumns.OrderBy(sc => sc.Value.Order))
            {
                // Get the property from the TModel, based on the key
                var prop = Expression.Property(parameter, sortedColumn.Key);

                // Build something like x => x.Cassette or x => x.SlotNumber
                var exp = Expression.Lamda(prop, parameter);

                // Based on the sorting direction, get the right method
                string method = String.Empty;
                if (firstTime)
                {
                    method = sortedColumn.Value.Direction == SortDirection.Ascending
                        ? "OrderBy"
                        : "OrderByDescending";

                    firstTime = false;
                }
                else
                {
                    method = sortedColumn.Value.Direction == SortDirection.Ascending
                        ? "ThenBy"
                        : "ThenByDescending";
                }

                // itemType is the type of the TModel
                // exp.Body.Type is the type of the property. Again, for Cassette, it's
                //     a String. For SlotNumber, it's a Double.
                Type[] types = new Type[] { itemType, exp.Body.Type };

                // Build the call expression
                // It will look something like:
                //     OrderBy*(x => x.Cassette) or Order*(x => x.SlotNumber)
                //     ThenBy*(x => x.Cassette) or ThenBy*(x => x.SlotNumber)
                var mce = Expression.Call(typeof(Queryable), method, types, 
                    collection.Expression, exp);

                // Now you can run the expression against the collection
                collection = collection.Provider.CreateQuery<TModel>(mce);
            }

            return collection;
        }
    }
}

注:OrderBy* 表示 OrderBy 或 OrderByDescending。ThenBy* 也是如此。

于 2019-04-25T20:24:03.877 回答
0

在 LINQ 中按多列排序的方法是先调用OrderBy()零次或多次调用ThenBy(). 您不能通过一次调用来做到这一点OrderBy()

例如,如果您想按列a和排序b,则必须生成一个类似于以下内容的表达式:

items.OrderBy(p => p.a).ThenBy(p => p.b)
于 2013-05-20T20:01:54.500 回答
-1

IQueryable 的以下扩展方法对我有用:

public static IQueryable<TEntity> OrderByColumns<TEntity>(
       this IQueryable<TEntity> collection,
       List<Sort> sortedColumns)
    {
        if (sortedColumns.Any())
        {
            var parameter = Expression.Parameter(typeof(TEntity));

            foreach (var sortedColumn in sortedColumns)
            {
                var prop = Expression.Property(parameter, sortedColumn.Name);

                var exp = Expression.Lambda(prop, parameter);
                string method = String.Empty;
                if (sortedColumns.First() == sortedColumn)
                {
                    method = sortedColumn.SortOrder == SortOrder.Ascending
                        ? "OrderBy"
                        : "OrderByDescending";
                }
                else
                {
                    method = sortedColumn.SortOrder == SortOrder.Ascending
                        ? "ThenBy"
                        : "ThenByDescending";
                }
                var orderbyExpression = Expression.Call(typeof(Queryable), method, new Type[] { typeof(TEntity), exp.Body.Type },
                  collection.Expression, exp);
                collection = collection.Provider.CreateQuery<TEntity>(orderbyExpression);
            }
        }
        return collection;
    }
}
于 2022-01-25T15:35:50.337 回答