Description
Is there an existing issue for this?
- I have searched the existing issues
Describe the bug
The new Microsoft.AspNetCore.OpenApi package yields invalid schema references when using recursive data models inside collections.
Here is an example of a recursive model Tree
because it contains a list of its own type in the property Children
:
record Tree
{
public required int Value { get; init; }
public required IReadOnlyCollection<Tree> Children { get; init; }
}
Return this through an endpoint, and the resulting schema is correct.
app.MapGet("/tree",
() => new Tree
{
Value = 1,
Children = []
});
// (partial) output:
"components": {
"schemas": {
"Tree": {
"required": [
"value",
"children"
],
"type": "object",
"properties": {
"value": {
"type": "integer",
"format": "int32"
},
"children": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Tree"
}
}
}
}
}
}
Notice here how the components.schemas.Tree.properties.children.items."$ref"
points back to Tree
- so far so good. The trouble comes when I add the recursive model in a collection in another model:
record Trees
{
public required List<Tree> List { get; init; }
}
app.MapGet("/trees",
() => new Trees { List = [] });
Two problems occur here. First of all, the Tree
model inside Tree.List
gets its own schema (called Tree2
in my case). The other problem is, the new components.schemas.Tree2.properties.children.items."$ref"
looks to refer back to the list of the enclosing Trees
model:
"components": {
"schemas": {
"Tree": {
// ...
},
"Tree2": { // <--- This shouldn't exist, could just re-use Tree
"required": [
"value",
"children"
],
"type": "object",
"properties": {
"value": {
"type": "integer",
"format": "int32"
},
"children": {
"type": "array",
"items": {
"$ref": "#/components/schemas/#/properties/list/items" // <--- This schema reference is incorrect
}
}
}
},
"Trees": {
"required": [
"list"
],
"type": "object",
"properties": {
"list": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Tree2"
}
}
}
}
}
}
}
If I only had the /trees/
endpoint, the Tree
schema would be necessary to generate of course. However, here it already exists. The schema reference is incorrect and should just point to Tree2
or ideally Tree
. I am not well-versed in the OpenAPI specification, so I determine it's "incorrect" because the Spectral Linter calls it out and Kiota is unable to parse this schema reference.
The same thing happens if I put Tree
inside a Dictionary. It generates this type of schema reference:
"$ref": "#/components/schemas/#/properties/lookup/additionalProperties/items"
Expected Behavior
Generate the schema without duplicate Tree
definitions and with correct schema references:
"components": {
"schemas": {
"Tree": {
"required": [
"value",
"children"
],
"type": "object",
"properties": {
"value": {
"type": "integer",
"format": "int32"
},
"children": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Tree"
}
}
}
},
"Trees": {
"required": [
"list"
],
"type": "object",
"properties": {
"list": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Tree"
}
}
}
}
}
}
}
Steps To Reproduce
I have created a minimal API showing the behavior:
https://gist.github.com/thorhj/933f07533ff1c8206ed7ebefd4d91c6a
In it I have included an endpoint returning just the recursive model Tree
(valid), the recursive model inside a List<Tree>
property (invalid) and the recursive data model inside a Dictionary<int, Tree>
(invalid).
Exceptions (if any)
No response
.NET Version
9.0.101
Anything else?
> dotnet --info
.NET SDK:
Version: 9.0.101
Commit: eedb237549
Workload version: 9.0.100-manifests.3068a692
MSBuild version: 17.12.12+1cce77968
Runtime Environment:
OS Name: Windows
OS Version: 10.0.22631
OS Platform: Windows
RID: win-x64
Base Path: C:\Program Files\dotnet\sdk\9.0.101\
.NET workloads installed:
There are no installed workloads to display.
Configured to use loose manifests when installing new manifests.
Host:
Version: 9.0.0
Architecture: x64
Commit: 9d5a6a9aa4
.NET SDKs installed:
8.0.206 [C:\Program Files\dotnet\sdk]
8.0.404 [C:\Program Files\dotnet\sdk]
9.0.101 [C:\Program Files\dotnet\sdk]
.NET runtimes installed:
Microsoft.AspNetCore.App 8.0.6 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 8.0.11 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 9.0.0 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.NETCore.App 6.0.36 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 8.0.6 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 8.0.11 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 9.0.0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.WindowsDesktop.App 6.0.36 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 8.0.6 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 8.0.11 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 9.0.0 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Other architectures found:
None
Environment variables:
Not set
global.json file:
Not found
Learn more:
https://aka.ms/dotnet/info
Download .NET:
https://aka.ms/dotnet/download