如何为多列OrderBy表达式创建表达式树

问题描述 投票:0回答:2

我已经为我的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列的Expression进行排序,但却无法找到有用的东西。

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

.net lambda expression expression-trees
2个回答
1
投票

在LINQ中按多列排序可以通过调用OrderBy(),然后调用ThenBy()进行零次或多次调用来实现。你不能通过一次调用OrderBy()来做到这一点。

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

items.OrderBy(p => p.a).ThenBy(p => p.b)

0
投票

我不知道接受的答案是如何被接受的答案,因为OP正在询问如何为多列排序方案创建表达式树。

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

enter image description here

屏幕截图显示用户想要通过Cassette(这是一个string)和Slot Number(这是一个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 *也是如此。

© www.soinside.com 2019 - 2024. All rights reserved.