私はEntity Frameworkを使用しており、多数のレコードを繰り返し処理する問題に頻繁に対応しています。私の問題は、それらを一気に取り上げると、私はタイムアウトになる危険があるということです。一度に1つずつ取り出すと、文字通りすべてのレコードが個別のクエリになり、永遠にかかるようになります。バッチで結果を取得するためのIEnumerable拡張
結果をバッチで取得するLinq拡張を実装したいが、それでもIEnumerableとして使用できる。私はそれにキーのセット(私は引っ張っているすべてのレコードのプライマリID)、バッチサイズ(単純なオブジェクトの場合は高く、複雑なオブジェクトの場合は低い)、およびキーのセットを適用する方法を定義するFunc
レコードタイプT
のセットに変換します。私はこのようにそれを呼び出します。
//get the list of items to pull--in this case, a set of order numbers
List<int> orderNumbers = GetOrderNumbers();
//set the batch size
int batchSize = 100;
//loop through the set using BatchedSelector extension. Note the selection
//function at the end which allows me to
foreach (var order in dbContext.Orders.BatchedSelector(repairNumbers, batchSize, (o, k) => k.Contains(o.OrderNumber)))
{
//do things
}
は、ここに私のドラフトソリューションです:
/// <summary>
/// A Linq extension that fetches IEnumerable results in batches, aggregating queries
/// to improve EF performance. Operates transparently to application and acts like any
/// other IEnumerable.
/// </summary>
/// <typeparam name="T">Header record type</typeparam>
/// <param name="source">Full set of records</param>
/// <param name="keys">The set of keys that represent specific records to pull</param>
/// <param name="selector">Function that filters the result set to only those which match the key set</param>
/// /// <param name="maxBatchSize">Maximum number of records to pull in one query</param>
/// <returns></returns>
public static IEnumerable<T> BatchedSelector<T>(this IEnumerable<T> source, IEnumerable<int> keys, Func<T, IEnumerable<int>, bool> selector, int maxBatchSize)
{
//the index of the next key (or set of keys) to process--we start at 0 of course
int currentKeyIndex = 0;
//to provide some resiliance, we will allow the batch size to decrease if we encounter errors
int currentBatchSize = maxBatchSize;
int batchDecreaseAmount = Math.Max(1, maxBatchSize/10); //10%, but at least 1
//other starting variables; a list to hold results and the associated batch of keys
List<T> resultList = null;
IEnumerable<int> keyBatch = null;
//while there are still keys remaining, grab the next set of keys
while ((keyBatch = keys.Skip(currentKeyIndex).Take(currentBatchSize)).Count() > 0)
{
//try to fetch the results
try
{
resultList = source.Where(o => selector(o, keyBatch)).ToList(); // <-- this is where errors occur
currentKeyIndex += maxBatchSize; //increment key index to mark these keys as processed
}
catch
{
//decrease the batch size for our retry
currentBatchSize -= batchDecreaseAmount;
//if we've run out of batch overhead, throw the error
if (currentBatchSize <= 0) throw;
//otherwise, restart the loop
continue;
}
//since we've successfully gotten the set of keys, yield the results
foreach (var match in resultList) yield return match;
}
//the loop is over; we're done
yield break;
}
何らかの理由で、「どこで」節は効果がありません。正しいキーがkeyBatchにあることを確認しましたが、期待されるWHERE OrderNumber IN (k1, k2, k3, kn)
行はありません。あたかもwhereステートメントがまったくないかのようです。
私は、式を作成してコンパイルする必要があると思っていますが、それが問題なのかどうかは分かりませんし、修正する方法もわかりません。入力が大好きです。ありがとう!