Dynamic LINQ,Select函数适用于Enumerable,但不适用于Queryable

时间:2012-12-08 13:13:22

标签: c# linq iqueryable expression enumerable

我一直在摆弄动态LINQ一段时间了,但我还没有了解它的秘密。

我有一个我要解析的表达式,如下所示:

"document.LineItems.Select(i => i.Credit).Sum();"

在解析这个问题时,我需要在LineItems Collection上调用Select函数。我正在使用Expression.Call的工厂方法:

Expression.Call(
    typeof(Queryable),
    "Select",
    new Type[] { typeof(LineItem), typeof(decimal?) },
    expr,
    Expression.Lambda(expression, new ParameterExpression[] { Expression.Parameter(typeof(LineItem) }));

此刻

expr:          document.LineItems    [ICollection<LineItem>]  
expression:    LineItem.Credit       [decimal?]

还没有实现。我现在正在构建表达式树。

现在,问题是:

此Expresssion.Call抛出异常:“没有泛型方法'选择'类型'System.Linq.Queryable'与提供的类型参数和参数兼容”;

通过改变Expression.Call的第一个参数,我可以通过在'System.Linq.Enumerable'而不是'Queryable'中查找'Select'来轻松解决它。

但是,这不是我想要的。我不希望所有的LineItem都只是计算Sum(),这显然是Enumerable的情况。我想让Queryable工作。

另外,对于解析的最后一部分 - Sum(),我还需要使用Enumerable Sum(),因为Queryable Sum()会抛出相同的Exception。

我已经检查了MSDN,“选择”功能的两个签名都是相同的,所以我真的看不出为什么会有一个工作而其他没有。

任何帮助或指示都会受到重视。

此致

1 个答案:

答案 0 :(得分:4)

问题是LineItemsICollection<LineItem>,而Queryable.Select的第一个参数需要IQueryable<LineItem>ICollection<T>仅实施IEnumerable<T>,这就是Enumerable.Select适用的原因。

您需要将expr的类型更改为IQueryable<LineItem>

您应该可以使用Queryable.AsQueryable方法执行此操作。以下函数创建一个表达式,以对给定Document实例的信用属性值求和:

public static Expression<Func<decimal?>> CreateSumLineItemsExpr(Document document)
{
    var docExpr = Expression.Constant(document);
    var itemsExpr = Expression.Property(docExpr, "LineItems");

    Expression<Func<LineItem, decimal?>> selector = i => i.Credit;
    var queryableExpr = Expression.Call(typeof(Queryable), "AsQueryable", new[] { typeof(LineItem) }, itemsExpr);
    var selectExpr = Expression.Call(typeof(Queryable), "Select", new[] { typeof(LineItem), typeof(decimal?) }, queryableExpr, selector);
    var sumExpr = Expression.Call(typeof(Queryable), "Sum", null, selectExpr);

    return Expression.Lambda<Func<decimal?>>(sumExpr);
}