非常に長時間実行されるプロセスを引き起こしているシナリオがあり、それがLINQtoEntityの使用であると思われます。
背景: プロジェクトは、LINQToEntityおよびRepositoryパターンを使用してデータをロジックレイヤーに公開しています。それが現状であり、変わることはありません。
問題: 入力と他のテーブルの両方から交差するデータを選択する必要がある特定のシナリオが発生しました。これを最適化するために、最初に、交差するデータを取得するために使用する予定のIDの配列をDBに照会しました。また、LINQ式で使用する整数の別の配列があります。次に、メソッドを含むLINQを使用して、問題のテーブルからデータを選択する式を作成します。これは実行に長い時間がかかります。ほぼ1分。
これと戦うために、私はすべてがほぼ同時にかかるいくつかの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でこれを行うためのより最適化された方法はありますか?
現時点では、このクエリを単純な結合としてDBに戻し、ここでLINQボトルネックをスキップすることを考えています。ただし、最初にフィルタリングされていないデータをメモリにプルしてから、LINQを使用してデータを結合し、それがどれほど効率的かを確認します。
アプリケーションのアーキテクチャを書き直さずに、他の人がどのようにして同様のボトルネックを克服したかに興味があります。
解決
コメント提供者が指摘したように、私の.ToArray()が原因でLINQの最適化は行われていませんでした。リポジトリの実装を使用して、結果をすでにIListにキャストしているtableTotalsIdsを構築していたため、問題はさらに深刻になり、LINQ/SQLの最適化がさらに失われました。リポジトリの実装を使用してtableTotalsIdsを構築し、dataContextを直接クエリしないで、結果をIQueryableのままにして問題を修正しました。