【问题标题】:Ineffective generation of SQL queries when using expressions with LINQ使用带有 LINQ 的表达式时生成的 SQL 查询无效
【发布时间】:2020-08-20 12:48:44
【问题描述】:

考虑以下代码,其中dbContext 是 SQL Server 数据库上下文,ExamplesDbSet

this.dbContext.Examples.Take(5).ToList();
Enumerable.Take(this.dbContext.Examples, 5).ToList();

第一行按预期工作,并通过以下方式转换为 SQL:

SELECT TOP(5) * FROM Examples

但是,第二行首先获取所有行,然后应用 Take 运算符。这是为什么呢?

由于我使用表达式来构建动态 lambda,我必须使用第二种方法 (Enumerable.Take):

var call = Expression.Call(
    typeof(Enumerable),
    "Take",
    new[]{ typeof(Examples) },
    contextParam,
    Expression.Constant(5)
);

不幸的是,第一种方法在使用表达式时不起作用,并且程序的当前架构迫使我动态构建 lambda。

为什么第二种方法会获取所有行以及如何防止它以有效地在表达式中使用它?

【问题讨论】:

  • 为什么你觉得它无效?你得到的正是你所要求的。枚举,然后过滤。没有什么低效的。这就像一个人在城市里以 150 公里/小时的速度行驶,然后抱怨他得到的罚单。
  • “因为我使用表达式来构建动态 lambda” - 不。这被称为不良程序。有办法交出一个lamba,所以不需要使用Enumerable。此外,您应该返回 IQueryable。

标签: c# sql sql-server linq


【解决方案1】:

您没有调用相同的方法。第一行是调用Queryable.Take,而不是Enumerable.Take

由于DbSet 实现了IQueryable<>IEnumerable<>,但IQueryable<> 实现了IEnumerable<>,编译器将IQueryable<> 视为更具体的类型。因此,当它解析要调用的Take 扩展方法时,它会确定Queryable.Take(...) 是正确的,因为它需要IQueryable<> 作为第一个参数。

这很重要,因为 IQueryable<> 接口允许将 LINQ 查询构建为表达式树,然后在 SQL 中求值。当您切换到将 IQueryable<> 视为 IEnumerable<> 时,您将失去该行为并切换到只能迭代之前构建的任何查询的结果。

试试这个:

Queryable.Take(this.dbContext.Examples, 5).ToList();

或者这个:

var call = Expression.Call(
    typeof(Queryable),
    "Take",
    new[]{ typeof(Examples) },
    contextParam,
    Expression.Constant(5)
);

【讨论】:

    【解决方案2】:

    它有效,因为在第一个语句中

    dbContext.Examples.Take(5).ToList(); - 您正在 IQuerable 接口上调用 .Take(5),LINQ to SQL 提供程序可以在该接口上对数据库执行适当的 SQL 语句。

    如果您需要在数据库端进行查询,则必须在 IQuerable 接口实例上构造查询。

    Enumerable.Take 是 IEnumarble 引用,Take 方法的执行将发生在内存中,在您从 db 中获取所有数据后

    【讨论】:

      【解决方案3】:

      "this.dbContext.Examples" 确实获取所有数据,然后是 Enumerable.Take 过滤器并从中获取前 5 个数据。

      【讨论】:

      • 不完全。 this.dbContext.ExamplesDbSet,所以它本身不会做任何事情。但是,当Enumerable.Take 开始对其进行迭代时,它就会被转换为获取所有数据的查询。
      猜你喜欢
      • 1970-01-01
      • 2015-04-27
      • 1970-01-01
      • 2019-05-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-10-16
      • 1970-01-01
      相关资源
      最近更新 更多