Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Null object when paging nested objects #8073

Closed
aviiliix opened this issue Feb 27, 2025 · 1 comment
Closed

Null object when paging nested objects #8073

aviiliix opened this issue Feb 27, 2025 · 1 comment

Comments

@aviiliix
Copy link

aviiliix commented Feb 27, 2025

Product

Hot Chocolate

Version

15.0.3

Link to minimal reproduction

https://gist.github.com/aviiliix/12fa5c64de0bf119b5744eb28dc1c33a

Steps to reproduce

SDK & Packages:

  • HotChocolate: 15.0.3
  • .NET: 9
  • Entity Framework: 9.0.2
  • Automapper:

I use a combination of Implementation First to handle certain directives (database context, includes, and projection) and CodeFirst to handle descriptors (paging, filtering, projection directives).

The entities arrangement like this:

TestRuns has an ICollection of Executions; which are projected into TestRunReponse with deep handling of each of its objects nested to objects of type ...Response.

TestRunResponse:

public class TestRunResponse : BaseResponse
{
..
    public ICollection<ExecutionResponse?>? Executions { get; init; }
    public ICollection<LabelResponse?>? Labels { get; init; }
    public ICollection<RunParamResponse?>? RunParams { get; init; }
...
}

Startup:

services.AddGraphQLServer()
		        .RegisterDbContextFactory<ReadDatabaseContext>()	
		        .AddQueryType<GraphQueryType>()					
		        .AddType<TestRunResponseType>()				
		        .AddFiltering()									
		        .AddSorting()								
		        .AddProjections()									
		        .AddAuthorization();

GraphQueryHandler (The resultant objects for the query are mapped to TestRunResponse:

	public IQueryable<TestRunResponse> TestRuns([Service] ReadDatabaseContext databaseContext)
	{
		// Include necessary relationships to prevent lazy loading and optimize performance.
		return databaseContext.TestRuns
		                      .Include(t => t.TestRunExecutions)
		                      .Include(t => t.TestRunLabels)
		                      .Include(t => t.TestRunParams)
		                      .Include(t => t.Platform)
		                      .AsNoTracking()
		                      .ProjectTo<TestRunResponse>(Mapper.ConfigurationProvider);
	}

GraphQueryType:ObjectType (Support for paging the nested object ):

protected override void Configure(IObjectTypeDescriptor<GraphQueryHandler> descriptor)
{
		// Defines a field for fetching test runs, returning a TestRunResponseType.
		descriptor.Field(f => f.TestRuns(default!))
		          .Type<TestRunResponseType>()
		          .UseOffsetPaging(options: new PagingOptions
		                                    {
			                                    IncludeTotalCount = true,
			                                    MaxPageSize = 50,
			                                    AllowBackwardPagination = true
		                                    })
		          .UseProjection()
		          .UseFiltering()
		          .UseSorting();
}

TestRunResponseType : ObjectType:

{
	/// <summary>
	/// Configures the GraphQL TestRunResponse type, enabling filtering on the "Executions" field.
	/// </summary>
	/// <param name="descriptor">Descriptor used to define fields and configurations for the type.</param>
	protected override void Configure(IObjectTypeDescriptor<TestRunResponse> descriptor)
	{
		// Enables filtering on the "Executions" field using HotChocolate.
		descriptor.Field(t => t.Executions)
		          .UsePaging(options: new PagingOptions
		                              {
			                              IncludeTotalCount = true,
			                              MaxPageSize = 50,
			                              AllowBackwardPagination = true
		                              })
		          .UseFiltering()
		          .UseSorting();
	}
}

What is expected?

The nested objects returned over the query needs to be NotNull and Populated

What is actually happening?

Once I add support for paging nested objects in a query, the response object is always null, meanwhile In a normal query (without paging nested objects) the resultant objects are populated (right behaviour)

Relevant log output

Additional context

Query:

query { testRuns(take: 10, skip: 0) { items { id version originType platform { name } executions( first: 5 ) { edges { node { id } cursor } pageInfo { endCursor hasNextPage } } runParams { key value } } pageInfo { hasNextPage hasPreviousPage } totalCount } }

Response:

{ "data": { "testRuns": { "items": [ { "id": "13c451f3-680d-42dd-ab8b-2339f0c1fecc", "version": "584v10-584010", "originType": "CLOUD", "platform": { "name": "CV-ADR" }, "executions": null, // <---- Here lies the error "runParams": [ { "key": "ipCharles", "value": "-ipsp 0.0.0.0" }, { "key": "portAppium", "value": "-sp 4728" }, ....

@aviiliix
Copy link
Author

Solved with the implementation of custom Resolver:

public IQueryable<ExecutionResponse> Executions(
	[Parent] TestRunResponse testRun,
	[Service] ReadDatabaseContext databaseContext)
{
	return databaseContext.TestRunExecutions
	                      .AsNoTracking()
	                      .Where(e => e.TestRunId.Equals(testRun.Id))
	                      .ProjectTo<ExecutionResponse>(Mapper.ConfigurationProvider);
}
// Configures the "Executions" field of the TestRunResponse type.
// Enables resolution using the specific resolver to obtain the executions.
descriptor.Field(t => t.Executions)
          .ResolveWith<TestRunResolver>(r => r.Executions(default!, default!)) 

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant