如何将LINQ查询的一部分移动到可重用的LINQ表达式树
我想通过使用表达式树来重复使用LINQ查询的一部分(我认为这就是它的调用方式)。
这是我的查询的简化版本:
var lQuery = from m in ... join a in ... into ta from ra in ta.DefaultIfEmpty() select new { ... Status = ra != null ? ... : ..., /* <- i want this to be reusable */ ... };
如您所见,Status的值是通过? :
? :
语法由LINQ自动翻译(我想是一个表达式树,SQL日志最后显示一个CASE WHEN
查询)。
如何将该代码移动到单独的函数,以便我的应用程序不会在运行时抛出not supported
exception?
我尝试添加一个function
public static System.Linq.Expressions.Expression<System.Func> QueryStatusExpression() { return ra => ra != null ? ... : ...; }
然后像它一样使用它
Status = QueryStatusExpression().Invoke(ra)
但它抛出了not supported
exception。
我现在没有想法。 任何帮助表示赞赏。
这个问题可以通过LINQKit项目的combined expressions
来解决。
这是怎么做的:
-
将
LinqKit
引用添加到项目中(例如,通过NuGet包管理器)。 -
在.cs文件的顶部添加以下行,以使LINQKit的扩展方法可用
using LinqKit; // for System.Linq.Expressions.Expression<>.Invoke()
-
将表达式定义为静态字段
public static System.Linq.Expressions.Expression< System.Func<%type of ra% ra, %status type%>> GetStatusExpression = ra != null ? ... : ...;
-
在查询中调用表达式(确保将.AsExpandable()添加到第一个表中,如LINQKit文档中所述)
var lQuery = from m in %first table%.AsExpandable() ... join a in ... into ta from ra in ta.DefaultIfEmpty() select new { ... Status = GetStatusExpression.Invoke(ra), ... };
如果你想在“普通”代码中使用表达式,那么你需要先编译它
public static System.Func<%type of ra% ra, %status type%> GetStatusExpressionCompiled = GetStatusExpression.Compile(); ... if (GetStatusExpressionCompiled(ra) == ...) { ...
非常感谢svick指出我正确的方向与它的评论。
编辑我的答案,因为我没有发现EF翻译问题。
使用vanilla代码,当你尝试抽象它时,你将总是有一个函数委托来处理(因为你的表达式将成为一个Expression
),这意味着查询提供者将要当它发现自己无法解释函数委托时会阻塞。 为了解决这个问题,你将不得不依赖像LINQKit这样的库来允许你将表达式合并在一起,或者基本上自己编写它们。 (见这个讨论 )。
似乎这段代码的动机是基本上执行左连接,并为没有匹配的行替换默认值而不是null。 我认为在这种情况下,有必要了解你想要完成的事情。 你最好在一层上下移动逻辑。 您可以将默认值逻辑移动到数据库中的视图或存储过程中以合并它。 您也可以先使用结果集然后对内存中的对象进行查询来使用您想要的方法,这样您就不必担心它可以转换为SQL查询了。