【问题标题】:Swagger crashes with circular model referencesSwagger 因循环模型参考而崩溃
【发布时间】:2018-12-10 12:01:55
【问题描述】:

我有一个 .Net Core 2.2 OData API,我正准备为此实现 Swagger 文档。

我一直在关注这个例子:https://github.com/Microsoft/aspnet-api-versioning/tree/master/samples/aspnetcore/SwaggerODataSample

我已经让它工作到一定程度。 现在我遇到了一个问题,我的模型以循环方式相互引用,让我解释一下:

注意:我使用 EFCore 代码优先方法来处理我的数据库。

我有这些模型(例如):Project、ProjectLocation、ProjectRegion。 让我们称它们为 A、B 和 C 以保持简短。

A 像这样引用 B 和 C:

public virtual ICollection<X> X{ get; set; }

B 和 C 都直接引用 A:

public A A{ get; set; }

这都是非常标准的关系数据库模型,但 SwaggerUI 似乎无法处理。

我得到的错误如下:

未能加载 API 定义。 错误: 获取错误: 内部服务器错误 /swagger/v1/swagger.json

处理请求时发生未处理的异常。 TypeLoadException:无法从程序集“Tc2fc56a7babe40419a678a075439246c.DynamicModels,Version=0.0.0.0,Culture=neutral,PublicKeyToken=null”加载类型“NCCRD.Services.DataV2.Database.Models.ProjectLocation”。

System.Signature.GetSignature(Void* pCorSig, int cCorSig, RuntimeFieldHandleInternal fieldHandle, IRuntimeMethodInfo methodHandle, RuntimeType declaringType)

如果需要,也可以使用堆栈跟踪。

我已尽我所能阅读了这两篇文章和所有参考链接,但没有找到可行的解决方案:

https://github.com/swagger-api/swagger-codegen/issues/741

https://github.com/swagger-api/swagger-codegen/issues/728

曾经看起来不错的解决方案是:https://systemout.net/2017/04/07/swagger-asp-net-core-fixing-circular-self-references/,但这也没有效果。 (我也联系过作者寻求帮助)

当我从 B 或 C 中删除对 A 的引用时,SwaggerUI 就会正常加载。

如果有人能对此有所了解,我将不胜感激。 在此先感谢您提供的任何帮助。

【问题讨论】:

  • 不返回或接受 WebApi 端点上的实体。
  • 我已经这样做了,而且必须这样做。这是客户使用的现有 API,我只是想将 Swagger 添加到组合中。使用 Swashbuckle 曾经很容易,但因为我现在同时使用 Odata 和 .Net Core,所以 Swashbuckle 不再是一个选项,afaik。
  • 可能是 OData API Explorer 中的一个错误。大多数(如果不是全部)Swagger 生成器使用反射来执行它们的工作,而 OData 使用 EDM 来确定模型在网络上的样子。因此,从相应的 CLR 类型生成的 Swagger 文档可能与 EDM 类型不同,这将是错误。 OData API Explorer 在需要时使用与 EDM 类型匹配的代理项替换 CLR 类型。正在调查此问题的原因。同时,解决方法应该是使用与 EDM 完全匹配的响应类型。

标签: asp.net-core odata swagger


【解决方案1】:

经过两天的深入研究和实验,我找不到任何开箱即用的解决方案。 Swashbuckle 曾经是我的首选,但因为我在 .NetCore 中使用 Odata,所以这不是一个选项。 Swashbuckle 分别为 OData 和 .NetCore 提供选项,但遗憾的是不能同时使用。

然而,我确实找到了一个解决方案,通过一些努力,我能够转换成适合我需要的东西。以下帖子构成了我最终解决方案的起点(顺便说一句,这仍在进行中):https://stackoverflow.com/a/51774147/4261155

我的“CustomDocumentFilter”版本已在 GitHub 上发布:https://github.com/SAEONData/NCCRD/blob/swagger_odata_netcore/NCCRD_API/NCCRD.Services.DataV2/Database/Contexts/CustomDocumentFilter.cs

如前所述,这仍是一项正在进行的工作,因此将在接下来的几天内进行更改,但与原始版本一起,我希望这可以帮助处于相同情况的其他人。另请注意,本课程已针对我的特定需求进行了调整,并非旨在为其他任何人提供“开箱即用”的解决方案。

【讨论】:

    【解决方案2】:

    这似乎是与Model Substitution 功能相关的错误。该修复程序已在 Microsoft.AspNetCore.OData.Versioning.ApiExplorer 包的 3.1.1 版本中发布。如果此问题仍然存在,请在 API 版本控制存储库中报告 new issue

    【讨论】:

    • 谢谢 Chris,我不知道什么时候才能有时间重温这篇文章,但我最终会谈到它,也许在几个月后。
    【解决方案3】:

    我使用 linq 序列化了响应。

    这是我的课程:

    public sealed class ProductDto
    {
        public int ProductId { get; set; }
        public string Description { get; set; }
        public string Type { get; set; }
    
        public List<PlanDto> Plans { get; set; }
    }
    
    public class PlanDto
    {
        public int PlanId { get; set; }
        public int ProductId { get; set; }
        public string Description { get; set; }
        public bool Active { get; set; }
        public DateTime Created { get; set; }
    
        public virtual ProductDto Product { get; set; }
    }
    

    然后我从响应中删除了在这种情况下为 Product = null 的循环引用。

    我在创建 ProductDto Api 响应时的解决方案:

    productEntity.ForEach(i => i.Plans.ForEach(j => j.Product = null));
    return productEntity;
    

    当 swagger 修复了这个问题时,这个序列化就没有必要了。

    【讨论】:

    • 我不明白这个解决方案。你在哪里添加 productEntity.ForEach(i => i.Plans.ForEach(j => j.Product = null));?
    • 如果我们看到类,一个计划有一个关联的产品。而且,产品有一个计划列表。因此,何时将数据返回给客户端。我说:我将退回此产品“productEntity”及其所有计划。但是,当需要将关联的产品返回到每个计划时,我将它们设置为空。 “Plans.ForEach(j => j.Product = null)”。这样:产品有计划列表,每个计划没有产品。试试看,告诉我结果如何。
    猜你喜欢
    • 2014-05-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-28
    • 2011-06-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多