InvalidOperationException:无法使用 schemaId ..相同的 schemaId 已用于类型

问题描述 投票:0回答:7

我收到以下错误。

InvalidOperationException: Can't use schemaId "$Registration" for type "$PortalService.Models.Registration". The same schemaId is already used for type "$PortalService.Models.Registration"

我已尝试以下链接中的建议,但没有成功。

招摇错误:schemaIds 冲突:检测到类型 A 和 B 的重复 schemaIds

我在模型中只有一个注册类。我尝试过重命名该类,但没有成功。

我正在使用 OData .Net Core 3.1 项目。

配置Swagger如下

 services.AddTransient<IConfigureOptions<SwaggerGenOptions>, ConfigureSwaggerOptions>();
            services.AddSwaggerGen(c =>
            {
                c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
                {
                    Description = @"JWT Authorization header using the Bearer scheme. \r\n\r\n 
                      Enter 'Bearer' [space] and then your token in the text input below.
                      \r\n\r\nExample: 'Bearer 12345abcdef'",
                    Name = "Authorization",
                    In = ParameterLocation.Header,
                    Type = SecuritySchemeType.ApiKey,
                    Scheme = "Bearer"
                });

                c.AddSecurityRequirement(new OpenApiSecurityRequirement()
                  {
                    {
                      new OpenApiSecurityScheme
                      {
                        Reference = new OpenApiReference
                          {
                            Type = ReferenceType.SecurityScheme,
                            Id = "Bearer"
                          },
                          Scheme = "oauth2",
                          Name = "Bearer",
                          In = ParameterLocation.Header,

                        },
                        new List<string>()
                      }
                    });
            });

使用 Swagger 如下

  app.UseSwagger(c =>
            {
                //c.PreSerializeFilters.Add((swaggerDoc, httpReq) => swaggerDoc.BasePath = basepath);

                 
                c.PreSerializeFilters.Add((swaggerDoc, httpReq) => {
                    Microsoft.OpenApi.Models.OpenApiPaths paths = new Microsoft.OpenApi.Models.OpenApiPaths();
                    foreach (var path in swaggerDoc.Paths)
                    {
                        paths.Add(path.Key.Replace(path.Key, basepath + path.Key), path.Value);
                    }
                    swaggerDoc.Paths = paths;
                });
            });
            app.UseSwaggerUI(
                options =>
                {
                    options.RoutePrefix = string.Empty;

                    // build a swagger endpoint for each discovered API version

                    foreach (var description in provider.ApiVersionDescriptions)
                    {
                        options.SwaggerEndpoint($"{basepath}/swagger/{description.GroupName}/swagger.json", description.GroupName.ToUpperInvariant());
                    }

                });

这似乎与

有关

Swagger 因循环模型引用而崩溃

我发现,如果我注释掉注册中的合作伙伴反向引用,错误就会消失,但我需要此引用。我不清楚如何解决这种情况。

[ForeignKey("Partner")]
[DataMember(Name = "PartnerOID")]
[Column(TypeName = "VARCHAR(100)")]
public string PartnerOID { get; set; }
//public virtual Partner Partner { get; set; }
c# swagger odata asp.net-core-3.1
7个回答
142
投票

试试这个约翰:https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/1607#issuecomment-607170559 帮助了我。

我能理解正在发生的事情;我有几个带有“状态”或“类型”的枚举。 使用

options.CustomSchemaIds( type => type.ToString() );
解决了这个问题。


98
投票

唯一需要更改的是方法中的

Startup.cs
ConfigureServices

您应该添加以下内容:

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

7
投票

我一直在使用

options.CustomSchemaIds(type => type.ToString());
options.CustomSchemaIds(type => $"{type.Name}_{System.Guid.NewGuid().ToString().Replace("-", "")}")
来创建模式名称的唯一性。两者都会导致我讨厌更长的模式名称。

这是一种跟踪名称重复的不同方法,我更喜欢这种方法。

助手类:

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

   public static string GetSchemaId(Type type)
   {
      string id = type.Name;

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

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

      return type.Name + (count > 1 ? count.ToString() : "");
   }
}

用途:

options.CustomSchemaIds(type => SwashbuckleSchemaHelper.GetSchemaId(type));

如果类名重复,结果如下。

  • 发票
  • 行项目
  • 状态
  • 状态2

3
投票

这是一个稍微灵活的解决方案

options.CustomSchemaIds(type => SwashbuckleSchemaHelper.GetSchemaId(type));

帮手

public static class SwashbuckleSchemaHelper
{
    private static readonly string _rootNamespace;
    private static readonly string _dtoFolder = "Dtos";

    static SwashbuckleSchemaHelper()
    {
        _rootNamespace = Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().ManifestModule.Name);
    }

    private static string GetRelativeNamespace(string typeNamespace)
    {
        if (!typeNamespace.StartsWith(_rootNamespace))
        {
            return typeNamespace;
        }

        var relativenamespace = typeNamespace.Substring(_rootNamespace.Length + _dtoFolder.Length + 1).TrimStart('.');
        if (string.IsNullOrEmpty(relativenamespace))
        {
            return string.Empty;
        }

        return $"{relativenamespace}.";
    }

    public static string GetSchemaId(Type type)
    {
        var schemaBase = $"{GetRelativeNamespace(type.Namespace)}{type.Name}";

        if (type.IsGenericType)
        {
            string? schemaGeneric;
            if (type.GenericTypeArguments.Length > 0)
            {
                var firstItem = type.GenericTypeArguments.First();
                schemaGeneric = $"<{GetRelativeNamespace(firstItem.Namespace)}{firstItem.Name}>";
            }
            else
            {
                schemaGeneric = $"<{Guid.NewGuid()}>";
            }

            return $"{schemaBase}{schemaGeneric}";
        }

        return $"{schemaBase}";
    }
}

2
投票

为了获得一个稍微好一点的类型名称,我想出了这个:

  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() : "")}";
      }
  }

用法如下:

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

更多详情请点击这里:

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


0
投票

刚刚遇到这个问题,这就是我想出的支持泛型的方法:

c.CustomSchemaIds(type =>
{
    if (!type.IsGenericType)
    {
        return type.Name;
    }

    var typeName = type.Name[..type.Name.IndexOf("`", StringComparison.Ordinal)];

    var genericTypeNames = type.GetGenericArguments().Select(a => a.Name);
    var genericNamesJoined = string.Join(".", genericTypeNames);

    return $"{typeName}.{genericNamesJoined}";
});

所以像这样的类型

Wrapper<FirstGeneric, SecondGeneric>
会有 schemaId
Wrapper.FirstGeneric.SecondGeneric

与这里的其他一些答案类似,但我觉得这段代码更容易理解。


-1
投票

这是我的版本

SwashbuckleSchemaHelper

public static class SwashbuckleSchemaHelper
{
    public static string GetSchemaId(Type type)
    {
        var sb = new StringBuilder();

        sb.Append(type.Namespace);
        sb.Append('.');
        sb.Append(type.Name);

        if (type.IsGenericType)
        {
            sb.Append('<');

            var arguments = type.GenericTypeArguments
                .Select(GetSchemaId)
                .ToArray();

            sb.Append(string.Join(',', arguments));

            sb.Append('>');
        }

        return sb.ToString();
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.