public override void OnConfigure( IDescriptorContext context, IObjectFieldDescriptor descriptor, MemberInfo member) { descriptor.Resolve(async ctx => { ICharacter character = ctx.Parent <ICharacter>(); ICharacterRepository repository = ctx.Service <ICharacterRepository>(); //This is injected by the PreProcessing middleware wen enabled... var graphQLParams = new GraphQLParamsContext(ctx); //******************************************************************************** //Perform some pre-processed retrieval of data from the Repository... //Notice Pagination processing is pushed down to the Repository layer also! //Get RepoDb specific mapper for the GraphQL parameter context... //Note: It's important that we map to the DB Model (not the GraphQL model). var repoDbParams = new GraphQLRepoDbMapper <CharacterDbModel>(graphQLParams); //Now we can retrieve the related and paginated data from the Repo... var pagedFriends = await repository.GetCharacterFriendsAsync(character.Id, repoDbParams.GetCursorPagingParameters()); return(new PreProcessedCursorSlice <ICharacter>(pagedFriends)); //******************************************************************************** }); }
protected override ValueTask <Connection> SliceAsync(IResolverContext context, object source, CursorPagingArguments arguments) { //If Appropriate we handle the values here to ensure that no post-processing is done other than // correctly mapping the results into a GraphQL Connection as Edges with Cursors... if (source is IPreProcessedCursorSlice <TEntity> pagedResults) { bool includeTotalCountEnabled = this.PagingOptions.IncludeTotalCount ?? PagingDefaults.IncludeTotalCount; var graphQLParamsContext = new GraphQLParamsContext(context); //Optimized to only require TotalCount value if the query actually requested it! if (includeTotalCountEnabled && graphQLParamsContext.IsTotalCountRequested && pagedResults.TotalCount == null) { throw new InvalidOperationException($"Total Count is requested in the query, but was not provided with the results [{this.GetType().GetTypeName()}] from the resolvers pre-processing logic; TotalCount is null."); } int?totalCount = pagedResults.TotalCount; //Ensure we are null safe and return a valid empty list by default. IReadOnlyList <IndexEdge <TEntity> > selectedEdges = pagedResults?.ToEdgeResults().ToList() ?? new List <IndexEdge <TEntity> >();; IndexEdge <TEntity>?firstEdge = selectedEdges.FirstOrDefault(); IndexEdge <TEntity>?lastEdge = selectedEdges.LastOrDefault(); var connectionPageInfo = new ConnectionPageInfo( hasNextPage: pagedResults?.HasNextPage ?? false, hasPreviousPage: pagedResults?.HasPreviousPage ?? false, startCursor: firstEdge?.Cursor, endCursor: lastEdge?.Cursor, totalCount: totalCount ?? 0 ); var graphQLConnection = new Connection <TEntity>( selectedEdges, connectionPageInfo, ct => new ValueTask <int>(connectionPageInfo.TotalCount ?? 0) ); return(new ValueTask <Connection>(graphQLConnection)); } throw new GraphQLException($"[{nameof(PreProcessedCursorPagingHandler<TEntity>)}] cannot handle the specified data source of type [{source.GetType().Name}]."); }
public override void OnConfigure( IDescriptorContext context, IObjectFieldDescriptor descriptor, MemberInfo member) { descriptor.Resolver(ctx => { ICharacter character = ctx.Parent <ICharacter>(); ICharacterRepository repository = ctx.Service <ICharacterRepository>(); //******************************************************************************** //Perform some pre-processed Paging (FYI, without sorting this may be unprdeicatble // but works here due to the in-memory store used by Star Wars example! var graphQLParams = new GraphQLParamsContext(ctx); var friends = repository.GetCharacters(character.Friends.ToArray()); var pagedFriends = friends.SliceAsCursorPage(graphQLParams.PagingArgs); return(new PreProcessedCursorSlice <ICharacter>(pagedFriends)); //******************************************************************************** }); }
protected override ValueTask <CollectionSegment> SliceAsync(IResolverContext context, object source, OffsetPagingArguments arguments) { //If Appropriate we handle the values here to ensure that no post-processing is done other than // correctly mapping the results into a GraphQL Collection Segment with appropriate Paging Details... if (source is IPreProcessedOffsetPageResults <TEntity> pagedResults) { bool includeTotalCountEnabled = this.PagingOptions.IncludeTotalCount ?? PagingDefaults.IncludeTotalCount; var graphQLParamsContext = new GraphQLParamsContext(context); //Optimized to only require TotalCount value if the query actually requested it! if (includeTotalCountEnabled && graphQLParamsContext.IsTotalCountRequested && pagedResults.TotalCount == null) { throw new InvalidOperationException($"Total Count is requested in the query, but was not provided with the results [{this.GetType().GetTypeName()}] from the resolvers pre-processing logic; TotalCount is null."); } int?totalCount = pagedResults.TotalCount; //Ensure we are null safe and return a valid empty list by default. var segmentResults = pagedResults?.ToList() ?? new List <TEntity>(); var collectionSegmentInfo = new CollectionSegmentInfo( hasNextPage: pagedResults?.HasNextPage ?? false, hasPreviousPage: pagedResults?.HasPreviousPage ?? false ); var graphQLConnection = new CollectionSegment( (IReadOnlyCollection <object>)segmentResults, collectionSegmentInfo, ct => new ValueTask <int>(totalCount ?? throw new InvalidOperationException()) ); return(new ValueTask <CollectionSegment>(graphQLConnection)); } throw new GraphQLException($"[{nameof(PreProcessedOffsetPagingHandler<TEntity>)}] cannot handle the specified data source of type [{source.GetType().Name}]."); }