async ValueTask <IPage> IPagingHandler.SliceAsync( IResolverContext context, object source) { int?skip = context.ArgumentValue <int?>(OffsetPagingArgumentNames.Skip); int?take = context.ArgumentValue <int?>(OffsetPagingArgumentNames.Take); var arguments = new OffsetPagingArguments(skip, take ?? DefaultPageSize); return(await SliceAsync(context, source, arguments).ConfigureAwait(false)); }
protected override ValueTask <CollectionSegment> SliceAsync( IResolverContext context, object source, OffsetPagingArguments arguments) { return(source switch { IQueryable <TItemType> q => ResolveAsync(context, q, arguments), IEnumerable <TItemType> e => ResolveAsync(context, e.AsQueryable(), arguments), IExecutable <TItemType> ex => SliceAsync(context, ex.Source, arguments), _ => throw new GraphQLException("Cannot handle the specified data source.") });
protected override async ValueTask <CollectionSegment> SliceAsync( IResolverContext context, object source, OffsetPagingArguments arguments) { IQueryable <TItemType> queryable = source switch { IQueryable <TItemType> q => q, IEnumerable <TItemType> e => e.AsQueryable(), _ => throw new GraphQLException("Cannot handle the specified data source.") }; IQueryable <TItemType> original = queryable; if (arguments.Skip.HasValue) { queryable = queryable.Skip(arguments.Skip.Value); } queryable = queryable.Take(arguments.Take + 1); List <TItemType> items = await ExecuteQueryableAsync(queryable, context.RequestAborted) .ConfigureAwait(false); var pageInfo = new CollectionSegmentInfo( items.Count == arguments.Take + 1, (arguments.Skip ?? 0) > 0); if (items.Count > arguments.Take) { items.RemoveAt(arguments.Take); } return(new CollectionSegment((IReadOnlyCollection <object>)items, pageInfo, CountAsync)); async ValueTask <int> CountAsync(CancellationToken cancellationToken) => await Task.Run(original.Count, cancellationToken).ConfigureAwait(false); }
/// <summary> /// Applies the pagination algorithm to the provided data. /// </summary> /// <param name="query">The query builder.</param> /// <param name="arguments">The paging arguments.</param> /// <param name="totalCount">Specify the total amount of elements</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns></returns> public async ValueTask <CollectionSegment <TEntity> > ApplyPaginationAsync( TQuery query, OffsetPagingArguments arguments, int?totalCount, CancellationToken cancellationToken) { Func <CancellationToken, ValueTask <int> > getTotalCount = totalCount is null ? async ct => await CountAsync(query, ct) : _ => new ValueTask <int>(totalCount.Value); TQuery sliced = query; if (arguments.Skip is { } skip) { sliced = ApplySkip(sliced, skip); } if (arguments.Take is { } take) { sliced = ApplyTake(sliced, take + 1); } IReadOnlyList <TEntity> items = await ExecuteAsync(sliced, cancellationToken).ConfigureAwait(false); bool hasNextPage = items.Count == arguments.Take + 1; bool hasPreviousPage = (arguments.Skip ?? 0) > 0; CollectionSegmentInfo pageInfo = new(hasNextPage, hasPreviousPage); items = new SkipLastCollection <TEntity>(items, skipLast: hasNextPage); return(new CollectionSegment <TEntity>( items, pageInfo, getTotalCount)); }
protected override async ValueTask <CollectionSegment> SliceAsync( IResolverContext context, object source, OffsetPagingArguments arguments) { IQueryable <TItemType> queryable = source switch { IQueryable <TItemType> q => q, IEnumerable <TItemType> e => e.AsQueryable(), _ => throw new GraphQLException("Cannot handle the specified data source.") }; IQueryable <TItemType> original = queryable; if (arguments.Skip.HasValue) { queryable = queryable.Skip(arguments.Skip.Value); } queryable = queryable.Take(arguments.Take + 1); List <TItemType> items = await ExecuteQueryableAsync(queryable, context.RequestAborted) .ConfigureAwait(false); var pageInfo = new CollectionSegmentInfo( items.Count == arguments.Take + 1, (arguments.Skip ?? 0) > 0); if (items.Count > arguments.Take) { items.RemoveAt(arguments.Take); } Func <CancellationToken, ValueTask <int> > getTotalCount = ct => throw new InvalidOperationException(); // TotalCount is one of the heaviest operations. It is only necessary to load totalCount // when it is enabled (IncludeTotalCount) and when it is contained in the selection set. if (IncludeTotalCount && context.Field.Type is ObjectType objectType && context.FieldSelection.SelectionSet is {} selectionSet) { IReadOnlyList <IFieldSelection> selections = context .GetSelections(objectType, selectionSet, true); var includeTotalCount = false; for (var i = 0; i < selections.Count; i++) { if (selections[i].Field.Name.Value is "totalCount") { includeTotalCount = true; break; } } // When totalCount is included in the selection set we prefetch it, then capture the // count in a variable, to pass it into the clojure if (includeTotalCount) { var captureCount = original.Count(); getTotalCount = ct => new ValueTask <int>(captureCount); } } return(new CollectionSegment( (IReadOnlyCollection <object>)items, pageInfo, getTotalCount)); }
/// <summary> /// The algorithm defining how to slice data of the specified <paramref name="source"/>. /// </summary> /// <param name="context"> /// The resolver context of the execution field. /// </param> /// <param name="source"> /// The object representing the data source, collection, or query builder. /// </param> /// <param name="arguments"> /// The paging arguments provided by the user. /// </param> /// <returns> /// The <see cref="CollectionSegment"/> representing /// the slice of items belonging to the requested page. /// </returns> protected abstract ValueTask <CollectionSegment> SliceAsync( IResolverContext context, object source, OffsetPagingArguments arguments);
/// <summary> /// Applies the pagination algorithm to the provided data. /// </summary> /// <param name="query">The query builder.</param> /// <param name="arguments">The paging arguments.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns></returns> public ValueTask <CollectionSegment <TEntity> > ApplyPaginationAsync( TQuery query, OffsetPagingArguments arguments, CancellationToken cancellationToken) => ApplyPaginationAsync(query, arguments, null, cancellationToken);