[英]Complex LINQ sorting using Lambda Expressions
是否有人擁有/知道采用表達式的IQueryable.OrderBy擴展(例如,通過反射進行檢索)? 我相信函數看起來像這樣:
public static IQueryable<TEntity> OrderBy<TEntity>
(this IQueryable<TEntity> source, Expression sortExpression)
假定Expression<Func<TEntity, T>>
為Expression<Func<TEntity, T>>
,其中TEntity是正在排序的同一對象,而T是創建新IQueryable時需要確定的類型。
我發現了很多采用字符串的擴展示例,包括Dynamic Linq,如下所示:
public static IQueryable<TEntity> OrderBy<TEntity>(
this IQueryable<TEntity> source, string sortExpression)
如果可以使用字符串並使用Reflection從相關對象中查找類型,則還可以使用Expression,並獲取Expression中正確的值類型。
以下是可能或不需要的我為什么想要這個的詳細說明。
我有很多要排序的復雜記錄。 因為列表很長,所以我更喜歡在數據庫端進行排序。 為了處理更復雜的屬性,我創建了提供排序功能的Expressions,如下所示:
if (model.sortExpression == "PlannedValue")
{
Expression<Func<BDopp, decimal>> sorter = BDopp.PlannedValueSorter;
if (model.sortDirection == "DESC")
opps = opps.OrderByDescending(sorter).AsQueryable();
else
opps = opps.OrderBy(sorter).AsQueryable();
}
BDOpp.PlannedValueSorter從對象檢索一個靜態表達式,該表達式允許進行排序而無需opps仍然是IQueryable類型:
public static Expression<Func<BDopp, decimal>> PlannedValueSorter
{
get
{
return z => z.BudgetSchedules
.Where(s => s.Type == 1)
.Sum(s => s.Value * s.Workshare * z.valueFactor / 100 / 100);
}
}
簡單屬性的排序是使用Extension方法完成的,這些方法使用Reflection基於字符串形式傳遞的屬性的名稱來構建表達式。
這很好用,但是對於復雜的類型,我仍然需要分支邏輯,而我寧願不這樣做。 我想做的是檢查包含表達式的靜態屬性,然后簡單地應用它。 我可以得到這樣的表達式:
PropertyInfo info = typeof(BDopp).GetProperty(model.sortExpression + "Sorter",
BindingFlags.Static | BindingFlags.Public);
Expression expr = (Expression)info.GetValue(null, null);
對於PlannedValue屬性,這使我獲得了在PlannedValueSorter中排序的表達式,我已經知道它可以工作。
各種挖掘使我得到了一些我認為可能會有所進步的信息:
public static IQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> source,
Expression<Func<TEntity, dynamic>> sortExpression)
{
var unary = sortExpression.Body as UnaryExpression;
Type actualExpressionType = unary.Operand.Type;
ActualExpressionType實際上是Expression的返回類型(對於此特定屬性,它是十進制)。
不幸的是,由於我還沒有全神貫注於這一切的工作原理,所以我主要是通過反復試驗來進行工作,因此我嘗試像這樣更新查詢無法正常工作:
MethodCallExpression resultExp = Expression.Call(typeof(Queryable),
"OrderBy",
new Type[] { typeof(TEntity), actualExpressionType },
source.Expression, sortExpression);
return source.Provider.CreateQuery<TEntity>(resultExp);
它可以編譯,但是在運行時會引發以下錯誤:
類型'System.Linq.Queryable'的通用方法'OrderBy'與提供的類型實參和實參不兼容。 如果方法是非泛型的,則不應該提供任何類型參數。
據我了解,您有一個Expression
並希望對其進行排序/過濾。 這可能會讓您感到驚訝,它是如此簡單:
Expression<Func<TEntity, T>> sortExpr = ...; //you already have this
var ordered = myQuery.OrderBy(sortExpr);
OrderBy 始終使用Expression
因此您可以直接使用它。 如果您只有一個Expression
類型的變量(指向一個Expression<Func<TEntity, T>>
類型的對象)並且不知道T
是什么,則可以執行以下操作:
Expression sortExpr = ...; //you already have this
var ordered = myQuery.OrderBy((dynamic)sortExpr);
dynamic
將在運行時使用反射來解決這一問題。 無需自己反思。 這里需要的只是過載解析(由C#運行時綁定程序執行)。
好的,我有一個解決方案:
public static IQueryable<TEntity> OrderBy<TEntity>(
this IQueryable<TEntity> source,
Expression<Func<TEntity, dynamic>> sortExpression,
bool descending)
{
var unary = sortExpression.Body as UnaryExpression;
var operand = unary.Operand;
Type actualExpressionType = operand.Type;
MethodCallExpression resultExp =
Expression.Call(typeof(Queryable),
descending? "OrderByDescending" : "OrderBy",
new Type[] { typeof(TEntity), actualExpressionType },
source.Expression,
Expression.Lambda(operand, sortExpression.Parameters));
return source.Provider.CreateQuery<TEntity>(resultExp);
}
布爾降序是為了允許標准OrderBy和OrderByDescending重載。 至少對我而言,這里有兩個重大突破:
Expression<Func<TEntity, T>>
語法要求您使用編譯時已知的類型。 嘗試這個:
public static IEnumerable<T> OrderBy<T>(
this IEnumerable<T> collection,
string columnName
//, SortDirection direction
)
{
ParameterExpression param = Expression.Parameter(typeof(T), "x"); // x
Expression property = Expression.Property(param, columnName); // x.ColumnName
Func<T, object> lambda = Expression.Lambda<Func<T, object>>( // x => x.ColumnName
Expression.Convert(property, typeof(object)),
param)
.Compile();
Func<IEnumerable<T>, Func<T, object>, IEnumerable<T>> expression = (c, f) => c.OrderBy(f); // here you can use OrderByDescending basing on SortDirection
IEnumerable<T> sorted = expression(collection, lambda);
return sorted;
}
用法:
IEnumerable<T> collection = ...
IEnumerable<T> ordered = collection.OrderBy("PropName");
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.