我有一个场景导致进程运行时间很长,我怀疑这是我们对 LINQ to Entity 的使用。
背景: 项目使用 LINQ To Entity and Repository 模式将数据暴露给我们的逻辑层。这就是它的现状,并且不会改变。
ISSUE: 出现了一个特定场景,需要从输入和其他表中选择相交数据。为了优化这一点,我首先在数据库中查询我打算用于获取相交数据的 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 优化。问题变得更深了,因为我正在使用我们的存储库实现来构建 tableTotalsIds,它已经将结果转换为 IList,失去了进一步的 LINQ/SQL 优化。只是不使用我们的 Repository 实现来构建 tableTotalsIds 并直接查询 dataContext,将结果保留为 IQueryable 解决了问题。