Ich benutze Entity Framework und häufig in Problem, wo ich eine große Anzahl von Datensätzen durchlaufen möchten. Mein Problem ist, dass wenn ich alle auf einmal ziehe, ich eine Auszeit riskiere; Wenn ich eins nach dem anderen ziehe, wird buchstäblich jeder einzelne Datensatz eine separate Abfrage sein und es dauert ewig.IEnumerable Erweiterung, um Ergebnisse in Batches zu ziehen
Ich möchte eine Linq-Erweiterung implementieren, die die Ergebnisse in Batches zieht, aber immer noch als IEnumerable verwendet werden kann. Ich würde ihm eine Reihe von Schlüsseln geben (höchstwahrscheinlich die primären IDs aller Datensätze, die ich ziehe), eine Stapelgröße (höher für einfache Objekte, niedriger für komplexe Objekte) und Func
, die definiert, wie ein Schlüsselsatz angewendet wird zu einem Datensatzsatz T
. Ich würde es so nennen:
//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
}
Hier ist mein Entwurf Lösung:
/// <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;
}
Aus irgendeinem Grund die „where“ -Klausel hat keine Wirkung. Ich habe validiert, dass die richtigen Schlüssel in keyBatch sind, aber die erwartete WHERE OrderNumber IN (k1, k2, k3, kn)
Zeile ist nicht da. Es ist so, als hätte ich die Wo-Aussage überhaupt nicht.
Meine beste Vermutung ist, dass ich den Ausdruck erstellen und kompilieren muss, aber ich bin mir nicht sicher, ob das das Problem ist und ich bin mir nicht sicher, wie ich es beheben soll. Würde jeden Input lieben. Vielen Dank!