/// <summary> /// Get results from paged query for NuGet v3 /// </summary> /// <typeparam name="B">Response body type</typeparam> /// <typeparam name="R">Result type</typeparam> /// <param name="request">Current request</param> /// <param name="getTotalResultsCountFromResponse">Delegate to get total result count from first response. Return 0 or a negative number to indicate failure.</param> /// <param name="getResultsFromResponse">Delegate to get results from a response body.</param> /// <param name="getPackageQuery">Delegate to get the next query, skipping a given number of results.</param> /// <param name="parseResponseBody">Delegate to parse the response string body to a response object body.</param> /// <returns>All results.</returns> public static IEnumerable <R> GetResults <B, R>(RequestWrapper request, Func <B, long> getTotalResultsCountFromResponse, Func <B, IEnumerable <IEnumerable <R> > > getResultsFromResponse, Func <long, string> getNextResponseQuery, Func <string, B> parseResponseBody, int pageCount) { ProgressTracker progressTracker = new ProgressTracker(ProgressTracker.GetRandomId(), 0, 100); int childProgressTrackerId = request.StartProgress(progressTracker.ProgressID, Resources.Messages.NuGetServerReadStarted); long total = -1; long resultCount = 0; bool successful = true; bool trackIndividualProgress = false; TaskGroup <IEnumerable <IEnumerable <R> > > taskGroup = new TaskGroup <IEnumerable <IEnumerable <R> > >(); System.Threading.CancellationTokenSource cancelToken = new System.Threading.CancellationTokenSource(); string query = getNextResponseQuery(resultCount); string content = new StreamReader(NuGetClient.DownloadDataToStream(query, request)).ReadToEnd(); B response = parseResponseBody(content); total = getTotalResultsCountFromResponse(response); if (total < 0) { total = -2; request.Warning(Messages.FailedToParseTotalHitsCount, query); successful = false; } else { request.Progress(childProgressTrackerId, 0, String.Format(CultureInfo.CurrentCulture, Resources.Messages.NuGetServerReadProgress, resultCount, total)); // When the result count is low enough, track individual results. Otherwise, track by page. trackIndividualProgress = total <= (pageCount * 2); taskGroup.Add(Task.Factory.StartNew <IEnumerable <IEnumerable <R> > >(() => { return(getResultsFromResponse(response)); }, cancelToken.Token)); } if (total >= 0) { long numberOfPages = total / pageCount + (total % pageCount == 0 ? 0 : 1); long pageSkipCount = 0; for (long pageNum = 1; pageNum < numberOfPages; pageNum++) { pageSkipCount += pageCount; string pageQuery = getNextResponseQuery(pageSkipCount); taskGroup.Add(Task.Factory.StartNew <IEnumerable <IEnumerable <R> > >((q) => { string pageContent = new StreamReader(NuGetClient.DownloadDataToStream((string)q, request)).ReadToEnd(); B pageResponse = parseResponseBody(pageContent); return(getResultsFromResponse(pageResponse)); }, pageQuery, cancelToken.Token)); } } while (taskGroup.HasAny) { if (request.IsCanceled()) { cancelToken.Cancel(); successful = false; break; } IEnumerable <IEnumerable <R> > resultCollections = taskGroup.WaitAny(); foreach (IEnumerable <R> resultCollection in resultCollections) { resultCount++; // If trackIndividualProgress == false, this progress will be reported at the end if (trackIndividualProgress) { // Report an individual package is done processing request.Progress(childProgressTrackerId, progressTracker.ConvertPercentToProgress(((double)resultCount) / total), String.Format(CultureInfo.CurrentCulture, Resources.Messages.NuGetServerReadProgress, resultCount, total)); } foreach (R result in resultCollection) { yield return(result); } } if (!trackIndividualProgress) { // Report that this page is done processing request.Progress(childProgressTrackerId, progressTracker.ConvertPercentToProgress(((double)resultCount) / total), String.Format(CultureInfo.CurrentCulture, Resources.Messages.NuGetServerReadProgress, resultCount, total)); } } request.CompleteProgress(childProgressTrackerId, successful); request.CompleteProgress(progressTracker.ProgressID, successful); }