对传递 Expression 与 Func 参数感到困惑

发布于 2024-12-24 23:15:33 字数 1194 浏览 3 评论 0 原文

我在理解表达式和函数工作方式之间的差异时遇到一些困难。 当有人将方法签名从以下位置更改

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 却不行?

I'm having some trouble understanding the differences between how Expressions and Funcs work.
This problem turned up when someone changed a method signature from:

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

To

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

Which broke my calling code. The old calling code (which worked) looked like this:

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

The new code (which doesn't work) looks like this:

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

This fails inside ThingList(...) on the line utilizing the expression:

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

With the runtime error:

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

This example is contrived, but my guess is it has something to do with the local object variable x not being properly "copied" into the expression.

Can someone explain how to handle this situation in general, and why the Func works but the Expression doesn't?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

梦里寻她 2024-12-31 23:15:34

几乎可以肯定,更改的原因是将谓词的计算“推送”到底层存储中,该存储支持您的上下文。更改后的 API 的作者决定使用 IQueryable,而不是将所有 Things 放入内存,然后使用 Func 来决定保留哪些内容。 ,并且需要一个 Expression> 来实现。

您对错误的起源是正确的:与内存谓词不同,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);

(您的后备存储几乎肯定可以理解整数),而不是说

The reason for the change almost certainly was to "push" the evaluation of your predicate into the underlying store, which backs your context. Instead of bringing all Things into memory and then using Func<Thing,bool> to decide which ones to keep, the author of the changed API decided to use IQueryable, and needed an Expression<Func<Thing,bool>> for that.

You are correct on the origin of the error: unlike in-memory predicates, IQueryable cannot use objects that it does not know, e.g. arbitrary instances of object.

What you need to do is to change the expression to avoid referencing objects of data types not supported by your target data store (I assume the expression eventually makes its way into either an Entity Framework or a Linq2Sql context). For example, instead of saying

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

you should say

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

(your backing store almost certainly understands integers)

一页 2024-12-31 23:15:34

表达式和 Func 之间的区别在此处的答案中得到了更好的描述:表达式与 Func 之间的区别>和 Func<>

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

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

The difference between Expression and Func is better described in the answers here: Difference between Expression<Func<>> and Func<>

A quick workaround to make this work again would be to compile the expression back into a Func.

var query = (from t in context.Things.Where(aWhere.Compile())
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文