-
Notifications
You must be signed in to change notification settings - Fork 10.4k
Microsoft.AspNetCore.OpenApi fails to generate document when using polymorphic types #58213
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
Comments
@keenjus Thanks for filing this issue, Martin! Your repro checks out for me. I believe the hiccup here is related to the polymorphic subtype referencing the parent in the generic type argument for the collection. I'll add a test case to our suite for this and see what the fix would look like.
We do support customizing the names for types, but unfortunately at the moment it won't allow you to modify the name of the polymorphic type entirely (e.g. BaseType + Subtype). :/ What kind of name would you prefer to see here? |
Ideally I would like derived types to use their type names, so instead of Full control over generated names would also be nice, so the current name generation wouldn't need changing. |
Following up on this: I'm moving this out of 9.0.0 since I need to spend a little bit more time fleshing out the fix and the window for bringing changes in .NET 9 is closing soon. This will have to come in a servicing patch for 9.0.
Other folks have asked for this so I'll open a separate API proposal for it. |
@captainsafia How do I influence the naming of schemas based on the .NET type? To remove a "Dto" suffix. I can't seem to find that in the documentation. I've tried the known transformers but no luck. I'm moving my 20+ services from NSwag. An equivalent to this: public class CustomSchemaNameGenerator : ISchemaNameGenerator
{
public string Generate(Type type)
{
if (type.IsGenericType)
{
return $"{type.Name.Replace("`1", string.Empty)}Of{GenerateName(type.GetGenericArguments().First())}";
}
return GenerateName(type);
}
private static string GenerateName(Type type)
{
return type.Name
.Replace("Dto", string.Empty);
//.Replace("Command", string.Empty)
//.Replace("Query", string.Empty);
}
} |
I dug into the source code and found that the actual name that will be used as reference id, or key, in the completed schema is stored under Annotations with the key I can modify that like so: options.AddSchemaTransformer(static (schema, context, ct) =>
{
const string SchemaId = "x-schema-id";
if (schema.Annotations?.TryGetValue(SchemaId, out var referenceIdObject) == true
&& referenceIdObject is string newReferenceId)
{
newReferenceId = GenerateSchemaName(context.JsonTypeInfo.Type);
schema.Annotations[SchemaId] = newReferenceId;
Console.WriteLine(newReferenceId);
}
return Task.CompletedTask;
}); The type I initially wondered why the Anyway, I'm starting to understand how this works. Not that different from any compiler having to keep track of references to types, members, and variables etc. 🙂 |
I'm also getting the same |
work with Swagger, when I try to migrate to OpenApi, get same error Swagger: builder.Services.AddSwaggerGen(options =>
{
options.SchemaGeneratorOptions.UseOneOfForPolymorphism = true;
options.SchemaGeneratorOptions.SubTypesSelector = baseType =>
baseType.GetCustomAttributes<JsonDerivedTypeAttribute>().Select(x => x.DerivedType);
options.SelectDiscriminatorNameUsing(baseType => baseType.GetCustomAttribute<JsonPolymorphicAttribute>()?.TypeDiscriminatorPropertyName ?? "$type");
Type? SearchParentRecursive<T>(Type type) where T : Attribute
{
if(type.BaseType is null || type == typeof(object))
{
return null;
}
var attribute = type.BaseType.GetCustomAttribute<T>();
if (attribute is null)
{
return SearchParentRecursive<T>(type.BaseType);
}
return type.BaseType;
}
options.SelectDiscriminatorValueUsing(subClass => SearchParentRecursive<JsonPolymorphicAttribute>(subClass)!.GetCustomAttributes<JsonDerivedTypeAttribute>().FirstOrDefault(x => x.DerivedType == subClass)!.TypeDiscriminator?.ToString());
}); |
I'm also experiencing issues generating OpenAPI documentation for polymorphic types. I want to bind a collection of a fairly complex polymorphic type from a request. It works perfectly fine when binding only a single instance. If I use We generate the documentation at compile-time, but I don't believe it makes any difference |
Any updates on this? |
Still a problem in 9.0.1! |
Facing the same issue |
I am facing the same issue. A workaround for me was to change my DTO types from: public record class BasePriceChangeDto(
IEnumerable<KiloWattPriceRange> BasePrices) : GroupChangeDto
public record class ProductRuleChangeDto(
ProductId ProductId,
IEnumerable<KiloWattPriceRange> PriceRanges) : GroupChangeDto;
public record struct KiloWattPriceRange(KiloWattRange KiloWattRange, Price Price); to: public record class BasePriceChangeDto(
IEnumerable<KiloWattPriceRange1> BasePrices) : GroupChangeDto
public record class ProductRuleChangeDto(
ProductId ProductId,
IEnumerable<KiloWattPriceRange2> PriceRanges) : GroupChangeDto;
public record struct KiloWattPriceRange1(KiloWattRange KiloWattRange, Price Price);
public record struct KiloWattPriceRange2(KiloWattRange KiloWattRange, Price Price); @captainsafia why is it that when using the same nested type (doesn't matter if it is I also tried converting |
This issue is blocking us from switching from Swashbuckle to .NET 9 (Microsoft.AspNetCore.OpenApi). |
I'm also having the same problem with Microsoft.AspNetCore.OpenApi 9.0.3 |
I've just re-tested this with Microsoft.AspNetCore.OpenApi 9.0.4 and the issue seems to have disappeared. Anyone else able to confirm? |
@abbottdev I just tested the 9.0.4 on a smaller project and it spit out a valid openapi doc. I am going to try it out on a larger project on Monday, but so far it looks promising. |
I think the AddOpenAPI might have just started ignoring the default JsonOptions, so even the bits that used to generate now don't. .Configure(o => fail: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1] |
Can also confirm that the 'An item with the same key has already been added' exception with polymorphic types in 9.0.4 no longer happens 🚀 |
still throw 'An item with the same key has already been added' exception with polymorphic types in 9.0.4 |
Still a problem for me on 9.0.5 |
Is there an existing issue for this?
Describe the bug
Using polymorphic types and returning both the base class and derived class in separate controller actions causes OpenApi document generation to fail.
Document generation succeeds if you disable/remove
public ComponentDto GetComponent()
action in the provided example repository.Also it seems like something is wrong with the generated document (when the exception causing controller action is removed). At most I would expect two model schemas to be generated
ComponentDto
ComponentDtoSectionDto
- does Microsoft.AspNetCore.OpenApi support custom names? I don't like the auto-generated one.but it also generates a weird looking
SectionDto
schema which seems like a weird mix ofComponentDto
&SectionDto
Expected Behavior
OpenApi document generation should not fail and should generate correct schemas when using polymorphic types.
Steps To Reproduce
https://github.com/keenjus/OpenApiStuff/tree/7d736b83d4ddf5b4208580b9d265d690f082d7b4
Launch the project and navigate to http://localhost:5052/openapi/v1.json. Should fail with "ArgumentException: An item with the same key has already been added. Key: ComponentDtoSectionDto "
Exceptions (if any)
.NET Version
9.0.100-rc.1.24452.12
Anything else?
Microsoft.AspNetCore.OpenApi 9.0.0-rtm.24501.7
The text was updated successfully, but these errors were encountered: