Skip to content

Provide a method to use type.ToString() instead of type.Name to disambiguate type name collisions in Open API by including namespace in schema name #64947

@DrLeh

Description

@DrLeh

Is there an existing issue for this?

  • I have searched the existing issues

Is your feature request related to a problem? Please describe the problem.

When open api docs are generated, if you have two types: MyOrg.App1.ContactView and MyOrg.App2.ContactView, these both get put into open api docs as "ContactView", even if there are differences between them.

In Swagger it was easy to disambiguate this:

options.CustomSchemaIds(x => x.ToString());

There is a method to customize these with Open API, but it requires copying the source code and modifying it to change one aspect of it. Or else you end up with a bunch of extraneous types like System.Collections.Generic.List showing in open api docs.

options.CreateSchemaReferenceId = t => t.Type.ToString();

The default implementation here has a lot of logic to get rid of these types, so I need to duplicate all that logic to make one small change to the naming convention for my types.

Describe the solution you'd like

Provide a func that allows developers to use type.ToString() instead of type.Name to disambiguate type name collisions in Open API, without having to replicate all the logic in OpenApiOptions.CreateDefaultSchemaReferenceId()

This is the code that needs to change, it's using type.Name, and I want to be able to choose to do type.ToString() instead.

if (type.IsGenericType)
{
// We need to handle the case where the generic type is a nested type,
// so we check if the name contains a backtick already.
// For more information: https://github.com/dotnet/aspnetcore/issues/59092
var backtickIndex = type.Name.LastIndexOf('`');
var isNestedGenericType = backtickIndex == -1;
var genericTypeName = isNestedGenericType ? type.Name : type.Name[..backtickIndex];
var genericArguments = type.GetGenericArguments();
var argumentNames = string.Join("And", genericArguments.Select(arg => arg.GetSchemaReferenceId(options)));
return $"{genericTypeName}Of{argumentNames}";
}
return type.Name;

Example of working code that solves the problem, with small refactors to use a local variable baseTypeName (typeName is used in another scope in this method)

        //var baseTypeName = type.Name; //existing functionality to replace
        var baseTypeName = type.ToString(); //this solves the namespace problem
        if (type.IsGenericType)
        {
            // We need to handle the case where the generic type is a nested type,
            // so we check if the name contains a backtick already.
            // For more information: https://github.com/dotnet/aspnetcore/issues/59092
            var backtickIndex = baseTypeName.LastIndexOf('`');
            var isNestedGenericType = backtickIndex == -1;

            var genericTypeName = isNestedGenericType ? baseTypeName : baseTypeName[..backtickIndex];
            var genericArguments = type.GetGenericArguments();
            var argumentNames = string.Join("And", genericArguments.Select(arg => arg.GetSchemaReferenceId(options)));
            return $"{genericTypeName}Of{argumentNames}";
        }
        return baseTypeName;

Requested Solution
Allow that top line of code to be provided as a Func<Type, string> in open api options and used in these lines. This allows me to have a one line fix to this, rather than have to copy all of the json extension methods used to generate these names with this one change.

//option 1- new property in the OpenApiOptions
options.CreateSchemaReferenceIdSelector = t => t.ToString();

//option 2- new overload for the "DefaultSchema" method that allows specifying how to get name from System.Type
options.CreateSchemaReferenceId = OpenApiOptions.CreateDefaultSchemaReferenceId(t => t.ToString());

Additional context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-minimalIncludes minimal APIs, endpoint filters, parameter binding, request delegate generator etcfeature-openapi

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions