我遇到这样一种情况,我的应用程序根据用户指定的过滤条件使用 PredicateBuilder 构建动态 LINQ 查询(旁白:查看此 link 以获得最佳 EF PredicateBuilder 实现)。问题是这个查询通常需要很长时间才能运行,我需要这个查询的结果来执行其他查询(即,将结果与其他表连接起来)。如果我正在编写 T-SQL,我会将第一个查询的结果放入一个临时表或一个表变量中,然后围绕它编写我的其他查询。我想从第一个查询中获取 ID 列表(例如 List<Int32> query1IDs
),然后执行如下操作:
var query2 = DbContext.TableName.Where(x => query1IDs.Contains(x.ID))
这在理论上可行;然而,query1IDs
中的 ID 数量可以是数百或数千(并且 LINQ 表达式 x => query1IDs.Contains(x.ID)
被翻译成 T-SQL“IN”语句,这很糟糕,原因很明显)和TableName 中的行数是 百万。有没有人对处理这种情况的最佳方式有任何建议?
编辑 1:关于我在做什么的额外说明。
好的,我正在构造我的第一个查询 (query1),它只包含我感兴趣的 ID。基本上,我将使用 query1 来“过滤”其他表。注意:我没有使用 ToList()
在 LINQ 语句的末尾---此时查询不执行并且没有结果发送到客户端:
var query1 = DbContext.TableName1.Where(ComplexFilterLogic).Select(x => x.ID)
然后我获取 query1 并使用它来过滤另一个表 (TableName2)。我现在输入 ToList()
在此语句的末尾,因为我想执行它并将结果带给客户端:
var query2 = (from a in DbContext.TableName2 join b in query1 on a.ID equals b.ID select new { b.Column1, b.column2, b.column3,...,b.columnM }).ToList();
然后我获取 query1 并重新使用它来过滤另一个表 (TableName3),执行它并将结果带给客户端:
var query3 = (from a in DbContext.TableName3 join b in query1 on a.ID equals b.ID select new { b.Column1, b.column2, b.column3,...,b.columnM }).ToList();
我可以对任意多的查询继续执行此操作:
var queryN = (from a in DbContext.TableNameN join b in query1 on a.ID equals b.ID select new { b.Column1, b.column2, b.column3,...,b.columnM }).ToList();
问题:query1 的执行时间很长。当我执行 query2、query3...queryN 时,query1 被执行 (N-1) 次...这不是一种非常有效的处理方式(特别是因为 query1 没有改变)。正如我之前所说,如果我正在编写 T-SQL,我会将 query1 的结果放入一个临时表中,然后在后续查询中使用该表。
编辑 2:
我要把回答这个问题的功劳归功于 Albin Sunnanbo 的评论:
When I had similar problems with a heavy query that I wanted to reuse in several other queries I always went back to the solution of creating a join in each query and put more effort in optimizing the query execution (mostly by tweaking my indexes).
我认为这确实是使用 Entity Framework 所能做的最好的事情。最后,如果性能变得非常糟糕,我可能会接受 John Wooley 的建议:
This may be a situation where dropping to native ADO against a stored proc returning multiple results and using an internal temp table might be your best option for this operation. Use EF for the other 90% of your app.
感谢所有对这篇文章发表评论的人...感谢大家的意见!
最佳答案
如果 TableName 的大小不太大而无法加载您使用的整个表
var tableNameById = DbContext.TableName.ToDictionary(x => x.ID);
以 ID
为键获取整个表并自动将其放入本地 Dictionary
中。
另一种方法是使用 .ToList()
“强制”LINQ 评估,在这种情况下获取整个表并使用 Linq2Objects 在本地执行 Where
部分。
var query1Lookup = new Hashset<int>(query1IDs);
var query2 = DbContext.TableName.ToList().Where(x => query1IDs.Contains(x.ID));
编辑:
将来自一个查询的 ID:s 列表存储在列表中并使用该列表作为另一个查询中的过滤器通常可以重写为连接。
当我想在其他几个查询中重用一个繁重的查询时遇到类似的问题时,我总是回到在每个查询中创建连接的解决方案,并投入更多精力来优化查询执行(主要是通过调整我的索引)。
关于c# - 在不重新查询数据库的情况下,在另一个 LINQ 查询中重用 LINQ 查询结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7585372/