Skip to content
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

SchemaId already used for different type #1607

Closed
MRVDH opened this issue Apr 1, 2020 · 12 comments
Closed

SchemaId already used for different type #1607

MRVDH opened this issue Apr 1, 2020 · 12 comments

Comments

@MRVDH
Copy link

MRVDH commented Apr 1, 2020

I'm getting this error because I think that for some reason it conflicts with a class that I have called Module. Changing the name of that class is not an option, so is there a way to fix this? Maybe the best question is; why does this error even occur at all?

Can't use schemaId "$Module" for type "$System.Reflection.Module". The same schemaId is already used for type "$Prjct.Data.Models.Module"

Swashbuckle.AspNetCore.Swagger version 5.2.1

Stacktrace:

Can't use schemaId "$Module" for type "$System.Reflection.Module". The same schemaId is already used for type "$SAM.Data.Models.Module"
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository.ReserveIdFor(Type type, String schemaId)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository.GetOrAdd(Type type, String schemaId, Func`1 factoryMethod)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateReferencedSchema(SerializerContract serializerContract, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateSchemaForType(Type type, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateSchema(Type type, SchemaRepository schemaRepository, MemberInfo memberInfo, ParameterInfo parameterInfo)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateObjectSchema(SerializerContract serializerContract, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateInlineSchema(SerializerContract serializerContract, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.<>c__DisplayClass6_0.<GenerateReferencedSchema>b__0()
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository.GetOrAdd(Type type, String schemaId, Func`1 factoryMethod)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateReferencedSchema(SerializerContract serializerContract, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateSchemaForType(Type type, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateSchema(Type type, SchemaRepository schemaRepository, MemberInfo memberInfo, ParameterInfo parameterInfo)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateArraySchema(SerializerContract serializerContract, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateInlineSchema(SerializerContract serializerContract, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateSchemaForType(Type type, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateSchema(Type type, SchemaRepository schemaRepository, MemberInfo memberInfo, ParameterInfo parameterInfo)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateObjectSchema(SerializerContract serializerContract, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateInlineSchema(SerializerContract serializerContract, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.<>c__DisplayClass6_0.<GenerateReferencedSchema>b__0()
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository.GetOrAdd(Type type, String schemaId, Func`1 factoryMethod)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateReferencedSchema(SerializerContract serializerContract, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateSchemaForType(Type type, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateSchema(Type type, SchemaRepository schemaRepository, MemberInfo memberInfo, ParameterInfo parameterInfo)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateArraySchema(SerializerContract serializerContract, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateInlineSchema(SerializerContract serializerContract, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateSchemaForType(Type type, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateSchema(Type type, SchemaRepository schemaRepository, MemberInfo memberInfo, ParameterInfo parameterInfo)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateObjectSchema(SerializerContract serializerContract, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateInlineSchema(SerializerContract serializerContract, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.<>c__DisplayClass6_0.<GenerateReferencedSchema>b__0()
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository.GetOrAdd(Type type, String schemaId, Func`1 factoryMethod)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateReferencedSchema(SerializerContract serializerContract, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateSchemaForType(Type type, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateSchema(Type type, SchemaRepository schemaRepository, MemberInfo memberInfo, ParameterInfo parameterInfo)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateObjectSchema(SerializerContract serializerContract, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateInlineSchema(SerializerContract serializerContract, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.<>c__DisplayClass6_0.<GenerateReferencedSchema>b__0()
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository.GetOrAdd(Type type, String schemaId, Func`1 factoryMethod)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateReferencedSchema(SerializerContract serializerContract, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateSchemaForType(Type type, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateSchema(Type type, SchemaRepository schemaRepository, MemberInfo memberInfo, ParameterInfo parameterInfo)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateObjectSchema(SerializerContract serializerContract, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateInlineSchema(SerializerContract serializerContract, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.<>c__DisplayClass6_0.<GenerateReferencedSchema>b__0()
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository.GetOrAdd(Type type, String schemaId, Func`1 factoryMethod)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateReferencedSchema(SerializerContract serializerContract, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateSchemaForType(Type type, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateSchema(Type type, SchemaRepository schemaRepository, MemberInfo memberInfo, ParameterInfo parameterInfo)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateArraySchema(SerializerContract serializerContract, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateInlineSchema(SerializerContract serializerContract, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateSchemaForType(Type type, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateSchema(Type type, SchemaRepository schemaRepository, MemberInfo memberInfo, ParameterInfo parameterInfo)
   at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GenerateRequestBodyFromBodyParameter(ApiDescription apiDescription, SchemaRepository schemaRepository, ApiParameterDescription bodyParameter)
   at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GenerateRequestBody(ApiDescription apiDescription, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GenerateOperation(ApiDescription apiDescription, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GenerateOperations(IEnumerable`1 apiDescriptions, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GeneratePaths(IEnumerable`1 apiDescriptions, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GetSwagger(String documentName, String host, String basePath)
   at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
   at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Builder.Extensions.MapMiddleware.Invoke(HttpContext context)
@MRVDH
Copy link
Author

MRVDH commented Apr 1, 2020

Fixed by using

services.AddSwaggerGen(options =>
{
    options.CustomSchemaIds(type => type.ToString());
});

From issue #1431: #1431 (comment)

@MRVDH MRVDH closed this as completed Apr 1, 2020
@MRVDH
Copy link
Author

MRVDH commented Mar 2, 2021

@domaindrivendev Not sure who the repo owner is but judging by the likes I'm not the only one with this issue. maybe it could get looked into?

@domaindrivendev
Copy link
Owner

By default, SB uses the short (unqualified name) which has its benefits because it keeps the docs simpler but also it’s downside if model names are duplicated in different namespaces. In this case, you can provide a custom naming strategy as described above.

That’s it! Not sure what else needs to be “looked into”. Are expecting SB to do something else out of the box? If so, please elaborate on proposed behavior?

@PureKrome
Copy link

PureKrome commented Mar 28, 2021

@domaindrivendev 👋🏻

Not sure what else needs to be “looked into”.

Instead of a 'global' setting using options.CustomSchemaIds .. is it possible to set this via an attribute on the Action Method?

something like:

// Details for a single user
[HttpGet("users/{userId}", Name = "Users_GetAUser")]
[ProducesResponseType(typeof(GetUser.Response), (int)HttpStatusCode.OK), Name = "Users_GetAUser"]
[ProducesResponseType((int)HttpStatusCode.NotFound)]
public async Task<IActionResult> GetUser([FromRoute] GetUser.Query request, CancellationToken cancellationToken)
{
 ...
}

// User index/search (query takes filter options)
[HttpGet("users/", Name = "Users_GetAUser")]
[ProducesResponseType(typeof(GetUsers.Response), (int)HttpStatusCode.OK), Name = "Users_GetUsers"]
public async Task<IActionResult> GetUsers([FromRoute] GetUsers.Query request, CancellationToken cancellationToken)
{
 ...
}

notice the Name = "Users_GetAUser" ? so we define the name of the response class, there.

@thesagarchavan
Copy link

thesagarchavan commented Jun 14, 2021

if you don't want to expose fullname of your model then

services.AddSwaggerGen(options =>
{
    options.CustomSchemaIds(type => $"{type.Name}_{System.Guid.NewGuid()}");
});

@johnnyreilly
Copy link

johnnyreilly commented Aug 31, 2022

To get a slightly nicer type name I came up with this:

  public class SwashbuckleSchemaHelper
  {
      private readonly Dictionary<string, int> _schemaNameRepetition = new();

      // borrowed from https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/95cb4d370e08e54eb04cf14e7e6388ca974a686e/src/Swashbuckle.AspNetCore.SwaggerGen/SchemaGenerator/SchemaGeneratorOptions.cs#L44
      private string DefaultSchemaIdSelector(Type modelType)
      {
          if (!modelType.IsConstructedGenericType) return modelType.Name.Replace("[]", "Array");

          var prefix = modelType.GetGenericArguments()
              .Select(genericArg => DefaultSchemaIdSelector(genericArg))
              .Aggregate((previous, current) => previous + current);

          return prefix + modelType.Name.Split('`').First();
      }

      public string GetSchemaId(Type modelType)
      {
          string id = DefaultSchemaIdSelector(modelType);

          if (!_schemaNameRepetition.ContainsKey(id))
              _schemaNameRepetition.Add(id, 0);

          int count = _schemaNameRepetition[id] + 1;
          _schemaNameRepetition[id] = count;

          return $"{id}{(count > 1 ? count.ToString() : "")}";
      }
  }

Usage looks like this:

services.AddSwaggerGen(options =>
{
    var schemaHelper = new SwashbuckleSchemaHelper();
    options.CustomSchemaIds(type => schemaHelper.GetSchemaId(type));
});

More details here:

https://blog.johnnyreilly.com/2022/08/31/swashbuckle-schemaid-already-used

@SGVoid
Copy link

SGVoid commented Sep 5, 2022

In my case, the issue was triggered by a couple of nested types with the same name, but their parents were completely different.

So,

services.AddSwaggerGen(options =>
{
    options.CustomSchemaIds(type => string.Join('.', new[] { type.ReflectedType?.Name, type.Name }.Where(x => x != null)));
});

worked fine for me

@icnocop
Copy link
Contributor

icnocop commented Sep 20, 2022

In my case, the issue was triggered by a couple of nested types with the same name, but their parents were completely different.

Swashbuckle.AspNetCore should be able to re-use the same schema id for the same types, even if they're nested in different classes, right?

I could not find another open issue for that scenario, so I think a new issue should be created.

@GlennPiper
Copy link

@johnnyreilly had a good idea, but I found that it kept incrementing the count with each view of the swagger page or file, so I made a bit of a tweak to it to make it able to be repeatably called:
`
private readonly Dictionary<string, List> _schemaNameRepetition = new();

    // borrowed from https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/95cb4d370e08e54eb04cf14e7e6388ca974a686e/src/Swashbuckle.AspNetCore.SwaggerGen/SchemaGenerator/SchemaGeneratorOptions.cs#L44
    private string DefaultSchemaIdSelector(Type modelType)
    {
        if (!modelType.IsConstructedGenericType) return modelType.Name.Replace("[]", "Array");

        var prefix = modelType.GetGenericArguments()
            .Select(genericArg => DefaultSchemaIdSelector(genericArg))
            .Aggregate((previous, current) => previous + current);

        return prefix + modelType.Name.Split('`').First();
    }

    public string GetSchemaId(Type modelType)
    {
        string id = DefaultSchemaIdSelector(modelType);

        if (!_schemaNameRepetition.ContainsKey(id))
            _schemaNameRepetition.Add(id, new List<string>());

        var modelNameList = _schemaNameRepetition[id];
        var fullName = modelType.FullName;
        if (!modelNameList.Contains(fullName))
            modelNameList.Add(fullName);

        int index = modelNameList.IndexOf(fullName);

        return $"{id}{(index >= 1 ? index.ToString() : "")}";
    }
}

`

@johnnyreilly
Copy link

johnnyreilly commented Sep 26, 2022

Thanks for sharing @GlennPiper - I use mine in script form and so I don't hit this. I can't quite make out what is different in yours... Can you clarify please?

I see it - have updated my blogpost and credited you @GlennPiper: https://blog.johnnyreilly.com/2022/08/31/swashbuckle-schemaid-already-used

@pdevito3
Copy link

also running into this, why would config.CustomSchemaIds(type => type.ToString()); not be the default behavior here?

@mshgh
Copy link

mshgh commented Nov 6, 2022

the same here ;) happy to found this discussion. In my case the Id can be made unique by adding last part of namespace, but what I have realized we do not need to copy&paste any source code from SB. It is actually available to us. So ultimate solution for my use-case is this

var defaultSchemaIdSelector = swaggerGenOptions.SchemaGeneratorOptions.SchemaIdSelector;
swaggerGenOptions.CustomSchemaIds(modelType => {
    var defaultId = defaultSchemaIdSelector(modelType);
    var prefix = modelType.Namespace?.Split(".").Last();
    return String.IsNullOrEmpty(prefix) ? defaultId : String.Concat(prefix, ".", defaultId);
});

I really love the idea of keeping list of types with collisions and use the index to make the Id unique. Just have you considered to store in the list the Type directly instead of its string representation? Dictionary<string, List<Type>>
I believe this will simplify it a bit.

@swashbuckle to me it looks like great idea to have this automatic number-based decoration as a part of the library. Not inside the SchemaIdSelector itself, but outside of it. This way everybody has opportunity to ensure there are unique meaningful Ids. But there will be fallback strategy in case the uniqueness fails. In this case adding sequence number is probably the best approach aligned with your requirement to keep the Ids short. To me much better then throwing an exception.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

10 participants