我的情况是我的应用程序使用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来“过滤”其他表。注意:我不在LINQ语句末尾使用ToList()
---此时查询不并且否< / strong>结果发送给客户:
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的评论来表达他的评价:
当我在一些重要的查询中遇到类似的问题时,我想在其他几个查询中重用它,我总是回到在每个查询中创建连接的解决方案,并且更加努力优化查询执行(主要是通过调整我的索引) )。
我认为这对于实体框架来说真的是最好的。最后,如果表演变得非常糟糕,我可能会选择John Wooley的建议:
这可能是这样一种情况:针对存储过程返回本机ADO返回多个结果并使用内部临时表可能是此操作的最佳选择。将EF用于其他90%的应用。
感谢所有对此帖发表评论的人......感谢大家的投入!
答案 0 :(得分:2)
如果TableName的大小不是太大而无法加载整个表,那么使用
var tableNameById = DbContext.TableName.ToDictionary(x => x.ID);
获取整个表格并自动将其放入以Dictionary
作为关键字的本地ID
。
另一种方法是使用.ToList()
“强制”LINQ评估,在获取整个表的情况下,使用Linq2Objects在本地执行Where
部分。
var query1Lookup = new Hashset<int>(query1IDs);
var query2 = DbContext.TableName.ToList().Where(x => query1IDs.Contains(x.ID));
修改强>
从列表中的一个查询存储ID:s列表并将该列表用作另一个查询中的过滤器通常可以重写为连接。
当我在一些重要的查询中遇到类似的问题,我想在其他几个查询中重用时,我总是回到在每个查询中创建连接的解决方案,并且更加努力优化查询执行(主要是通过调整我的索引)。 / p>
答案 1 :(得分:1)
由于您正在运行结果的后续查询,请执行第一个查询并将其用作SQL Server上的View,将视图添加到上下文中,并针对视图构建LINQ查询。
答案 2 :(得分:1)
您是否考虑过按照本文撰写查询(使用装饰器设计模式):
Composed LINQ Queries using the Decorator Pattern
前提是,不是枚举你的第一个(非常常见的)查询,而是基本上使用装饰器模式来生成IQueryable链,它是查询1和查询N的结果。这样你总是执行过滤后的表单查询。
希望这可能会有所帮助