/// <summary> /// Get all pages in a paginated collection. /// </summary> /// <remarks> /// If <paramref name="progress"/> is non-<see langword="null"/>, the first call to /// <see cref="IProgress{T}.Report"/> will specify the <paramref name="page"/> /// argument. After each task to obtain to the next page of results completes, /// the <see cref="IProgress{T}.Report"/> method will be called again with the /// new page of results. /// <para> /// This method determines that the end of the collection is reached when either of /// the following conditions is true. /// </para> /// <list type="bullet"> /// <item>The <see cref="ReadOnlyCollectionPage{T}.CanHaveNextPage"/> property returns <see langword="false"/>.</item> /// <item>An empty page is reached.</item> /// </list> /// </remarks> /// <typeparam name="T">The type of elements in the collection.</typeparam> /// <param name="page">The first page in the collection.</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/> that the task will observe.</param> /// <param name="progress">An optional callback object to receive progress notifications. If this is <see langword="null"/>, no progress notifications are sent.</param> /// <returns> /// A <see cref="Task"/> object representing the asynchronous operation. When the operation /// completes successfully, the <see cref="Task{TResult}.Result"/> property will contain a /// read-only collection containing the complete set of results from the paginated collection. /// </returns> /// <exception cref="ArgumentNullException">If <paramref name="page"/> is <see langword="null"/>.</exception> public static Task <ReadOnlyCollection <T> > GetAllPagesAsync <T>(this ReadOnlyCollectionPage <T> page, CancellationToken cancellationToken, IProgress <ReadOnlyCollectionPage <T> > progress) { if (page == null) { throw new ArgumentNullException("page"); } List <T> result = new List <T>(); ReadOnlyCollectionPage <T> currentPage = page; Func <bool> condition = () => currentPage != null; Func <Task> body = () => { if (progress != null) { progress.Report(currentPage); } result.AddRange(currentPage); if (currentPage.CanHaveNextPage && currentPage.Count > 0) { return(currentPage.GetNextPageAsync(cancellationToken) .Select(task => currentPage = task.Result)); } else { currentPage = null; return(CompletedTask.Default); } }; return(TaskBlocks.While(condition, body) .Select(_ => result.AsReadOnly())); }
/// <summary> /// Gets the next page in the paginated collection. /// </summary> /// <param name="page">A page in the collection.</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/> that the task will observe.</param> /// <returns> /// <para>A <see cref="Task"/> object representing the asynchronous operation. When the task /// completes successfully, the <see cref="Task{TResult}.Result"/> property will contain /// a collection containing the next page of results.</para> /// </returns> /// <exception cref="ArgumentNullException">If <paramref name="page"/> is <see langword="null"/>.</exception> /// <exception cref="InvalidOperationException">If <see cref="ReadOnlyCollectionPage{T}.CanHaveNextPage"/> is /// <see langword="false"/>.</exception> /// <exception cref="WebException">If the HTTP request does not return successfully.</exception> public static Task <ReadOnlyCollectionPage <T> > GetNextPageAsync <T>(this ReadOnlyCollectionPage <T> page, CancellationToken cancellationToken) { if (page == null) { throw new ArgumentNullException("page"); } return(TaskBlocks.Using( () => page.PrepareGetNextPageAsync(cancellationToken), task => task.Result.SendAsync(cancellationToken).Select(innerTask => innerTask.Result.Item2))); }