我已经为我的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表达式。
在LINQ中按多列排序可以通过调用OrderBy()
,然后调用ThenBy()
进行零次或多次调用来实现。你不能通过一次调用OrderBy()
来做到这一点。
例如,如果您想要按列a
和b
排序,则必须生成一个类似于以下内容的表达式:
items.OrderBy(p => p.a).ThenBy(p => p.b)
我不知道接受的答案是如何被接受的答案,因为OP正在询问如何为多列排序方案创建表达式树。
有时您必须使用表达式树手动构建OrderBy语句,因为您不知道用户想要排序的列。例如,当您使用Datatables构建网格并且某些列是可排序的时,用户可以SHIFT单击列标题以按多列排序:
屏幕截图显示用户想要通过Cassette(这是一个string
)和Slot Number(这是一个double
)对网格进行排序。
在构建用于排序的表达式树时,这里的棘手部分是你第一次需要使用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 *也是如此。