【问题标题】:To LINQ or not to LINQ: Regarding LINQ to Entity/SQL/Object到 LINQ 还是不到 LINQ:关于 LINQ to Entity/SQL/Object
【发布时间】:2012-05-09 11:54:50
【问题描述】:

我有一个场景导致进程运行时间很长,我怀疑这是我们对 LINQ to Entity 的使用。

背景: 项目使用 LINQ To Entity and Repository 模式将数据暴露给我们的逻辑层。就是这样,而且不会改变。

问题: 出现了某种场景,需要从输入和其他表中选择相交数据。为了优化这一点,我首先在数据库中查询我打算用于获取相交数据的 ID 数组。我还有另一个整数数组可以在我的 LINQ 表达式中使用。然后,我使用 LINQ where 和 contains 方法构建一个表达式以从相关表中选择数据。这需要很长时间才能执行。差不多一分钟。

为了解决这个问题,我尝试了几种 LINQ 技术,它们几乎都需要相同的时间。为方便起见,以下是我的一些方法的示例。

// FYI: tableTotalsIds contains 14,856 IDs as an example, built from a repository call
var tableTotalsIds = tableTotals.Select(s => s.Id).ToArray();
int[] ages = {25, 26, 27};

Expression<Func<TotalAgeCounts, bool>> ageFilter = 
    af => af.TableTotalsId != null &&
          tableTotalsIds.Contains(af.TableTotalsId.Value) &&
          ages.Contains(af.Age);

var directStartTime = DateTime.Now;
var directFetch = _ctx.TotalAgeCounts.Where(ageFilter).ToList();
var directBenchMark = DateTime.Now.Subtract(directStartTime).TotalSeconds;

var repositoryStartTime = DateTime.Now;
var repositoryFetch = _totalAgeCountsRepository
    .SelectAll(new Specification<TotalAgeCounts>(ageFilter));
var repositoryBenchMark = DateTime.Now.Subtract(repositoryStartTime).TotalSeconds;

在所有情况下,查询时间大约需要 1 分钟。让我大吃一惊的是 .Contains() 方法中使用了大量的 tableTotalsIds,但我不知道实现这一点的其他 LINQ 方法。

在 LINQ 中有更优化的方法吗?

目前我正在考虑将此查询作为简单的连接放回数据库,并在此处跳过 LINQ 瓶颈。但首先我会尝试将未过滤的数据拉入内存,然后使用 LINQ 将数据连接在一起,看看效率如何。

我对其他人如何在不重写应用程序架构的情况下克服类似瓶颈感兴趣。

解决方案

正如评论者所指出的,由于我的 .ToArray(),LINQ 优化没有发生。问题变得更深了,因为我正在使用我们的 Repository 实现来构建 tableTotalsIds,它已经将结果转换为 IList,失去了进一步的 LINQ/SQL 优化。只是不使用我们的 Repository 实现来构建 tableTotalsIds 并直接查询 dataContext,将结果保留为 IQueryable 解决了问题。

【问题讨论】:

  • 您是否首先从数据库中获取了总 ID?您是否需要将它们拉回来,然后将它们反馈回 LINQ,或者您可以在 DB 上完成所有这些操作吗?如果将“ToArray”放在 tablesTotalsId 上会发生什么?
  • @rup 正是我的第一个想法。 “优化”似乎是这里问题的很大一部分。在这里运行的查询并不复杂。
  • 目前,ID 是从数据库中提取的(这似乎是在浪费时间)。 ToArray 可以阻止它重新查询,所以我认为它应该让它更快(如果有的话)。获取 ID 并不需要很长时间,后面的查询就可以了。
  • 你倒退了。您要查询的数据在数据库中......这就是您想要的地方。您将其拉出并重新插入,而不允许查询优化器完成其工作。在最坏的情况下,删除 ToArray 不会有任何区别。充其量,这可能是你的问题。
  • 啊...试过了,但没有改变任何东西。当前实现的问题是应用程序使用的存储库已经将它变成了一个列表,所以它并没有真正改变任何东西。我将尝试直接跳过存储库和 LINQ 到数据库而不“优化”它,看看它是如何进行的。 :)

标签: .net sql linq entity-framework


【解决方案1】:

您调用ToArray 的事实导致过滤记录被拉出数据库,只是作为查询的一部分再次注入。这会阻止查询优化器充分利用它已经拥有所需内容的事实。正如您在评论中指出的那样,删除 Tolist/toarray 有所帮助。

至于存储库模式,你没有理由不能使用它。您只是不需要为每个类都建立一个单独的存储库;仅适用于您将查询的重要根对象。

在这种情况下,您的辅助表信息可以在同一个查询中汇总;存储库模式不要求您为其创建单独的存储库。

【讨论】:

  • 感谢您的解释。我收回我所说的关于放弃使用存储库的内容,并同意在这种情况下不需要它。我不知道 IQueryable 可以优化进一步的数据库调用。吸取教训!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-27
  • 2011-05-08
  • 2011-11-29
相关资源
最近更新 更多