对于传递Expression vs. Func参数感到困惑

时间:2012-01-04 14:20:42

标签: c# linq linq-to-entities

我在理解Expressions和Funcs工作方式之间的差异时遇到了一些麻烦。 有人从以下方法更改了方法签名时出现此问题:

public static List<Thing> ThingList(Func<Thing, bool> aWhere)

public static List<Thing> ThingList(Expression<Func<Thing, bool>> aWhere)

这破坏了我的通话代码。旧的调用代码(有效)看起来像这样:

        ...
        object y = new object();
        Func<Thing, bool> whereFunc = (p) => p == y;
        things = ThingManager.ThingList(whereFunc);

新代码(不起作用)如下所示:

        ...
        object x = new object();
        Expression<Func<Thing, bool>> whereExpr = (p) => p == x;
        things = ThingManager.ThingList(whereExpr);

在使用表达式的行上的ThingList(...)中失败:

        var query = (from t in context.Things.Where(aWhere)
        ...

运行时错误:

Unable to create a constant value of type 'System.Object'. Only primitive types ('such as Int32, String, and Guid') are supported in this context.

这个例子是设计的,但我的猜测是它与本地对象变量x没有被正确地“复制”到表达式中有关。

有人可以解释一般如何处理这种情况,以及为什么Func有效但Expression没有?

2 个答案:

答案 0 :(得分:13)

改变的原因几乎可以肯定是将谓词的评估“推”到底层商店,这会支持你的context。不是将所有Things放入内存,然后使用Func<Thing,bool>来决定保留哪些内容,更改后的API的作者决定使用IQueryable,并需要Expression<Func<Thing,bool>>这一点。

您对错误的来源是正确的:与内存中的谓词不同,IQueryable不能使用它不知道的对象,例如: object的任意实例。

您需要做的是更改表达式以避免引用目标数据存储不支持的数据类型的对象(我假设表达式最终进入实体框架或Linq2Sql上下文)。例如,而不是说

object x = new object();
Expression<Func<Thing, bool>> whereExpr = (p) => p == x;
things = ThingManager.ThingList(whereExpr);

你应该说

Thing x = new Thing {id = 123};
Expression<Func<Thing, bool>> whereExpr = (p) => p.id == x.id;
things = ThingManager.ThingList(whereExpr);

(你的支持商店几乎肯定理解整数)

答案 1 :(得分:6)

Expression和Func之间的区别在这里的答案中有更好的描述:Difference between Expression<Func<>> and Func<>

使这项工作重新开始的快速解决方法是将表达式编译回Func。

var query = (from t in context.Things.Where(aWhere.Compile())