我正在努力使表达式组合起来,环顾四周,应该有可能做我想做的事情,但是我很挣扎。
我有一个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);
我想做的就是将表达式中的通用逻辑提取到我可以重用的静态“ helper”样式表达式中,例如,而不是在第一个表达式中包含此行
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之类的表达式,不幸的是,当您有两个表达式,而不是一个表达式在内部 另一个表达式时,
我还尝试过使用here中提到的自定义ExpressionVisitors来实现包装/展开(我最终确实可以很好地编译它,但是得到了相同的错误)
我也尝试了许多其他类似的答案,并尝试使用LinqKit来Expand
我的表情,并使用AsExpandable
设置我的上下文无济于事。到目前为止,我所有的尝试都导致了以上错误,或者在尝试使用LambdaExpression.Lambda
进行合并的组合表达式进行合并时导致了该错误:
从作用域''引用的类型的变量'foo',但未定义
我认为这是因为在我的外部表达式foo
中使用的变量Expression<Func<foo, dynamic>>
在我的表达式Expression<Func<bar, bool>>
中不存在,因为将它们组合后的结果表达式没有任何意义。
不幸的是,我认为我对自己想做的事情没有足够深入的了解,因此我对于下一步的路途已陷入僵局。任何关于这是否可能和/或如何实现的建议,将不胜感激。
编辑:我已经使用Rextester创建了两个示例项目this is the working example和this is what causes the error-使用此在线工具,您实际上可以看到应该运行的EF,但是由于没有EF可以针对它运行测试期间实际上并没有发生错误。
答案 0 :(得分:-1)
您的代码
Expression<Func<foo, dynamic>> expression = foo => new
{ RecordExists = foo.bar.Exists,
RecordHasPaid = ExpressionHelper.BarHasPaid(foo.bar)
}
不返回期望的结果。 RecordHasPaid
的类型为Expression<Func<bar, bool>>
,而不仅仅是bool
。显然,EF对此感到困惑。
基本上,您想调用表达式内的另一个表达式,而不是返回lambda表达式。 不幸的是,对于AFAIK来说,使用LINQ语法是不可能的。
但是,如果您处理表达式树,则可以完成此工作。 在这里看看: Combining two lambda expressions in c#
与表情树打交道缺乏美感和类型安全性,但这是一把强大的剑。可惜编译器拒绝支持表达式树的全部功能。 Lambda调用以及循环都丢失了。