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

Eri*_*rix 19 c# linq linq-to-entities

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

public static List<Thing> ThingList(Func<Thing, bool> aWhere)
Run Code Online (Sandbox Code Playgroud)

public static List<Thing> ThingList(Expression<Func<Thing, bool>> aWhere)
Run Code Online (Sandbox Code Playgroud)

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

        ...
        object y = new object();
        Func<Thing, bool> whereFunc = (p) => p == y;
        things = ThingManager.ThingList(whereFunc);
Run Code Online (Sandbox Code Playgroud)

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

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

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

        var query = (from t in context.Things.Where(aWhere)
        ...
Run Code Online (Sandbox Code Playgroud)

运行时错误:

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

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

有人可以解释如何处理这种情况一般,为什么Func工程但Expression不是?

das*_*ght 13

几乎可以肯定的是,改变的原因是将谓词的评估"推"到底层商店,后者支持你的context.更改后的API的作者决定使用,而不是将所有内容都Things放入内存然后Func<Thing,bool>用来决定保留哪些内容.IQueryableExpression<Func<Thing,bool>>

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

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

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

你应该说

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

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


Nic*_*yle 7

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

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

var query = (from t in context.Things.Where(aWhere.Compile())
Run Code Online (Sandbox Code Playgroud)