【发布时间】:2013-03-08 20:14:15
【问题描述】:
是否有人拥有/知道采用表达式(例如,通过反射检索)的 IQueryable.OrderBy 扩展?我相信这个函数看起来像这样:
public static IQueryable<TEntity> OrderBy<TEntity>
(this IQueryable<TEntity> source, Expression sortExpression)
表达式将被假定为 Expression<Func<TEntity, T>>,其中 TEntity 是被排序的同一对象,而 T 是需要确定的类型才能创建新的 IQueryable。
我发现了许多采用字符串的扩展示例,包括 Dynamic Linq,如下所示:
public static IQueryable<TEntity> OrderBy<TEntity>(
this IQueryable<TEntity> source, string sortExpression)
如果可以获取字符串并使用反射从相关对象中查找类型,那么也应该可以获取表达式,并获取表达式中的值类型。
以下是我为什么想要这个的详细解释,你可能需要也可能不需要。
我有一个相当大的复杂记录列表要排序。因为列表很长,我更喜欢在数据库端进行排序。为了处理更复杂的属性,我创建了提供排序功能的表达式,如下所示:
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);
}
}
简单属性的排序是使用扩展方法完成的,这些方法使用反射来构建基于作为字符串传递的属性名称的表达式。
这很好用,但是对于复杂的类型,我仍然需要分支逻辑,我宁愿不这样做。我宁愿检查包含表达式的静态属性,然后简单地应用它。我可以得到这样的表达:
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 实际上是表达式的返回类型(对于这个特定的属性,它是十进制)。
不幸的是,我主要是通过反复试验来工作,因为我还没有完全理解这一切是如何工作的,所以我尝试像这样更新查询是行不通的:
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” 与提供的类型参数和参数兼容。无类型 如果方法是非泛型的,则应提供参数。
【问题讨论】:
-
普通的
OrderBy()已经接受了一个表达式。为什么你不能使用它? -
普通的OrderBy需要强类型的表达式,但是不设置一长串的分支条件,编译的时候是不知道类型的。
标签: c# .net linq expression-trees