예제 #1
0
        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));
        }
예제 #2
0
 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);
        }
예제 #4
0
        /// <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));
        }
예제 #6
0
 /// <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);
예제 #7
0
 /// <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);