Is there an existing issue for this?
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
Is there an existing issue for this?
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
Treebecause it contains a list of its own type in the propertyChildren:Return this through an endpoint, and the resulting schema is correct.
Notice here how the
components.schemas.Tree.properties.children.items."$ref"points back toTree- so far so good. The trouble comes when I add the recursive model in a collection in another model:Two problems occur here. First of all, the
Treemodel insideTree.Listgets its own schema (calledTree2in my case). The other problem is, the newcomponents.schemas.Tree2.properties.children.items."$ref"looks to refer back to the list of the enclosingTreesmodel:If I only had the
/trees/endpoint, theTreeschema would be necessary to generate of course. However, here it already exists. The schema reference is incorrect and should just point toTree2or ideallyTree. 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
Treeinside a Dictionary. It generates this type of schema reference:Expected Behavior
Generate the schema without duplicate
Treedefinitions and with correct schema references: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 aList<Tree>property (invalid) and the recursive data model inside aDictionary<int, Tree>(invalid).Exceptions (if any)
No response
.NET Version
9.0.101
Anything else?