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

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

public static List ThingList(Func aWhere) 

 public static List ThingList(Expression<Func> aWhere) 

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

  ... object y = new object(); Func whereFunc = (p) => p == y; things = ThingManager.ThingList(whereFunc); 

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

  ... object x = new object(); Expression<Func> 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没有?

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

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

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

 object x = new object(); Expression> whereExpr = (p) => p == x; things = ThingManager.ThingList(whereExpr); 

你应该说

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

(你的后备商店几乎可以理解整数)

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

再次使这项工作的快速解决方法是将表达式编译回Func。

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