【发布时间】:2019-01-11 14:22:12
【问题描述】:
我正在努力解决组合表达式的问题,环顾四周,应该可以做我想做的事情,但我很挣扎。
我有一个Expression<Func<class, dynamic>>,它定义了我最终针对我的 EF 上下文运行的选择,其中class 是我从中选择数据的表。例如:
Expression<Func<foo, dynamic>> expression = foo => new
{
RecordExists = foo.bar.Exists,
RecordHasPaid = foo.bar.Amount != null && foo.bar.Amount > 0
}
// run the expression against the database and get the results...
var results = context.foo.Select(expression);
我想要做的是将表达式中的通用逻辑提取到静态“帮助”样式表达式中,例如,我可以重复使用,而不是在我的第一个表达式中包含这一行
RecordHasPaid = foo.bar.Amount != null && foo.bar.Amount > 0
我可以有一个可重用的表达式,存储在一个可重用的类中,比如ExpressionHelper,然后我可以将其插入到我的第一个表达式中:
public static Expression<Func<bar, bool>> BarHasPaid(bar foobar)
{
return b => b.Amount != null && b.Amount > 0;
}
变成:
Expression<Func<foo, dynamic>> expression = foo => new
{
RecordExists = foo.bar.Exists,
RecordHasPaid = ExpressionHelper.BarHasPaid(foo.bar)
}
// run the expression against the database and get the results...
var results = context.foo.Select(expression);
请注意,我向这个表达式传递了一个与外部表达式不同的参数(希望我可以是通用的)。
听起来不错,编译得很好,但是 - 当我开始运行 .Select 时,我得到了可怕的错误:
LINQ to Entities 无法识别该方法 'System.Linq.Expressions.Expression1[System.Func2[bar,System.Boolean]] BarHasPaid(bar)' 方法,并且该方法不能翻译成 存储表达式。
我理解发生这种情况的原因(至少我认为是这样),因为我的方法未知,因此无法翻译成 SQL,此外,我的表达式无法转换为对 EF 有意义的表达式树,因为我我本质上是在使用两个不同的表达式,它不知道如何将它们变成有意义的东西。
我尝试了各种方法,将 here 等表达式组合在一起,不幸的是,当您有两个表达式而不是一个表达式inside另一个表达式时,这似乎可以工作。
我还尝试使用 here 中提到的自定义 ExpressionVisitor 实现包装/解包(我最终确实可以正常编译,但我得到了同样的错误)
我还尝试了许多其他类似的答案,并尝试使用 LinqKit 来 Expand 我的表达式,并将我的上下文设置为 AsExpandable 无济于事。到目前为止,我的所有尝试都导致了上述错误,或者在尝试使用 LambdaExpression.Lambda 组合来编译我的组合表达式时出现这个错误:
变量 'foo' 的类型从范围 '' 引用但未定义
我认为这是被抛出的,因为在我的外部表达式 Expression<Func<foo, dynamic>> 中使用的变量 foo 在我的表达式 Expression<Func<bar, bool>> 中不存在,因为当它们组合时生成的表达式没有意义。
不幸的是,我认为我对我正在尝试做的事情的理解不够深入,所以我在下一步转向哪里时遇到了一些死胡同。任何关于这是否可能和/或如何实现的建议将不胜感激。
编辑:我使用 Rextester 创建了两个示例项目,this is the working example 和 this is what causes the error - 使用这个在线工具,您实际上可以看到应该运行什么 EF,但是因为没有 EF 可以针对错误运行它并没有实际发生在该测试期间。
【问题讨论】:
-
@DannyLager,使用表达式 vistiors,并替换内部 lambda 中的参数。例如,当您编写
x => x.something并将其与x => x.somethingElse组合时,仅仅因为参数命名相同并不会使它们相同,您需要重写表达式以使用相同的参数 -
您对
BarHasPaid的定义似乎有问题 - 它有一个从未使用过的参数foobar? -
在尝试实现这个时,我遇到了一个更大的问题——当编译器编译
Expression时,它使用BarHasPaid的类型作为RecordHasPaid匿名成员的类型,即Expression<>不是boolean。在这一点上,你开始重写这么多Expression,你正在构建一个编译器。 -
@NetMage 表达式中的 func 不能包含与传入的参数相同的名称,知道 foobar 是 func 的参数并且你可以给它另一个别名就足够聪明了,这很烦人......关于 BarHasPaid 的类型作为 RecordHasPaid 的类型,我会对此进行一些挖掘
标签: c# lambda expression func linqkit