graphql-dotnet / graphql-dotnet/graphql-dotnet
Node `.Parent` skips containers of arrays
- Dominant language
- C#
- Stars
- 6k
- Forks
- 945
- Avg merge
- 3d 13h
- Merged PRs (30d)
- 14
Description
## Description
When traversing the `.Parent` property tree of an `IResolveFieldContext`, the parent of a `ListGraphType` will be omitted from the tree. This is easier to demonstrate than explain, please see [this gist](https://gist.github.com/dLopreiato/be949e52fd0aacf93d86ec29b80b5aa6).
### Steps to reproduce
1. Set up a graph schema that contains some `ListGraphType`.
2. In a resolver of `T`, execute `context.Parent`.
Expect the parent of `ListGraphType`. Get the `ListGraphType`.
The above linked gist demonstrates the scenario by recursively walking terminal nodes and creating a string of parent names walked. This demonstration is intended to make it easy to see, but the actual problem is with `.Parent`, not just `.ParentType`. (A `parentTree` property has also been provided that puts together a list of CLR types of the models or `.Source` types.)
This gist can also be used to reproduce this issue by putting a breakpoint in the resolver for `parentTree` and manually walking up the `.Parent` tree. None of the types in this walk will be `PingMulti`, when at least one should.
### Expected result
`.Parent` should return the container type of `ListGraphType`, not the `ListGraphType`. As demonstrated in the gist, something like this:
```json
{
"data": {
"multi": {
// Omitted for brevity
"list": [
{
"parentTypeTree": "Ping - PingMulti - Query"
},
// ...
]
}
}
}
```
### Actual result
`.Parent` actually returns the `ListGraphType`, then *that parent* returns a skipped parent (in the gist, this is `Query`), which means the container of `ListGraphType` (in this gist, this is `PingMulti`) is entirely skipped.
```json
{
"data": {
"multi": {
// Omitted for brevity
"list": [
{
"parentTypeTree": "Ping - %null - Query"
},
// ...
]
}
}
}
```
### Environment
Only tested on .NET 10, Ubuntu 24.04.4 LTS, x64 Architecture. I am able to repro on GraphQL lib versions 7.0.0, 8.8.4, and 8.8.5. (Source does not compile for versions earlier than 7.0.0.)
### Impact
Some of my graph types can only ever exist as a child to a specific entity (as exemplified in the gist as `PingType` for the child and `PingMultiType` for the parent). I never let any user query for `PingType` unless they are querying through `PingMultiType`. When resolving a `PingType` property, I want to access the model of `PingMultiType` for contextual information, so I walk the parent tree until `.Source` returns the model of the type I'm looking for (`PingMultiType.Model`). If `PingType` is stored as an array on `PingMultiType`, I will never find the model I'm looking for. For all other cases, this mechanism works as expected.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start by running the linked gist with a schema containing ListGraphType and inspect the resolver that walks IResolveFieldContext.Parent. Trace the Parent chain from a resolver for T and compare it with the expected parentTypeTree. Done means the container type between the list element and the query is preserved in the Parent tree, including its Source model.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- api
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 58/100