重用LINQ查询会导致另一个LINQ查询,而无需重新查询数据库

时间:2011-09-28 15:10:01

标签: c# entity-framework c#-4.0 entity-framework-4 entity-framework-4.1

我的情况是我的应用程序使用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%的应用。

感谢所有对此帖发表评论的人......感谢大家的投入!

3 个答案:

答案 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的结果。这样你总是执行过滤后的表单查询。

希望这可能会有所帮助