You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swashbuckle.AspNetCore处理Geometry类型触发OOM异常问题咨询

问题说明

使用Swashbuckle.AspNetCore处理NetTopologySuite的Geometry类型属性时触发System.OutOfMemoryException内存溢出,将对应属性改为string类型后功能正常。当前已通过SchemaFilter移除标记IgnoreDataMember特性的成员,通过DocumentFilter手动清理NetTopology相关Schema定义,但需要非DocumentFilter的方案,从根源阻止Swagger生成器处理这类非必要类型,避免内存溢出。

  • 问题属性:GeoPoly(Geometry类型,已标记[IgnoreDataMember])

现有Swagger配置代码

public static IServiceCollection AddSwaggerModule(this IServiceCollection services)
{
    services.AddSwaggerGen(c =>
    {
        c.SwaggerDoc("v2", new OpenApiInfo { Title = "Test API", Version = "0.0.1" });
        c.SchemaFilter<MySwaggerSchemaFilter>();
        c.DocumentFilter<RemoveBogusDefinitionsDocumentFilter>();
        c.ResolveConflictingActions(x => x.First());
    });

    return services;
}

public static IApplicationBuilder UseApplicationSwagger(this IApplicationBuilder app)
{
    app.UseSwagger(c =>
    {
        c.RouteTemplate = "{documentName}/api-docs";
    });
    app.UseSwaggerUI(c =>
    {
        c.SwaggerEndpoint("/v2/api-docs", "Test API");
    });
    return app;
}

public class MySwaggerSchemaFilter : Swashbuckle.AspNetCore.SwaggerGen.ISchemaFilter
{
    public void Apply(OpenApiSchema schema, SchemaFilterContext context)
    {
        if (schema?.Properties == null)
        {
            return;
        }

        var ignoreDataMemberProperties = context.Type.GetProperties()
            .Where(t => t.GetCustomAttribute<IgnoreDataMemberAttribute>() != null);

        foreach (var ignoreDataMemberProperty in ignoreDataMemberProperties)
        {
            var propertyToHide = schema.Properties.Keys
                .SingleOrDefault(x => x.ToLower() == ignoreDataMemberProperty.Name.ToLower());

            if (propertyToHide != null)
            {
                schema.Properties.Remove(propertyToHide);
            }
        }
    }
}

public class RemoveBogusDefinitionsDocumentFilter : Swashbuckle.AspNetCore.SwaggerGen.IDocumentFilter
{
    public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
    {
        swaggerDoc.Components.Schemas.Remove("Districts");
        swaggerDoc.Components.Schemas.Remove("Geometry");
        swaggerDoc.Components.Schemas.Remove("CoordinateSequenceFactory");
        swaggerDoc.Components.Schemas.Remove("GeometryOverlay");
        swaggerDoc.Components.Schemas.Remove("NtsGeometryServices");
        swaggerDoc.Components.Schemas.Remove("CoordinateEqualityComparer");
        swaggerDoc.Components.Schemas.Remove("NtsGeometryServices");
        swaggerDoc.Components.Schemas.Remove("GeometryFactory");
        swaggerDoc.Components.Schemas.Remove("OgcGeometryType");
        swaggerDoc.Components.Schemas.Remove("Coordinate");
        swaggerDoc.Components.Schemas.Remove("Point");
    }
}

涉及实体类代码

public class Districts : BaseEntity<long>
{
    public string DistrictsDesc { get; set; }
    public string DistrictsDescAr { get; set; }

    [IgnoreDataMember]
    [Column(TypeName = "geometry")]
    public Geometry GeoPoly { get; set; }
    public IList<Records> Records { get; set; } = new List<Records>();
    public long? RegionsId { get; set; }
    public Regions Regions { get; set; }

    public long? CitiesId { get; set; }
    public Cities Cities { get; set; }
}

解决方案

现有SchemaFilter、DocumentFilter方案不生效的核心原因:这两个Filter执行时机太晚,要等Swashbuckle完成类型递归遍历、Schema生成后才会触发。NetTopologySuite的Geometry相关类型存在大量循环引用,递归遍历阶段就会无限占满内存,根本走不到后续删Schema的逻辑。

可以用以下三种从根源阻断的方案,按落地成本从低到高排序:

  • 方案1:给属性加[JsonIgnore]特性
    Swashbuckle默认跟随JSON序列化规则扫描属性,[IgnoreDataMember]是DataContract序列化的特性,不会在类型扫描阶段被识别,无法阻断递归。直接给GeoPoly加JSON序列化忽略特性,扫描阶段就会跳过该属性,完全不会解析Geometry的类型结构:
// 用System.Text.Json就加这个,用Newtonsoft就加[Newtonsoft.Json.JsonIgnore]
[JsonIgnore]
[IgnoreDataMember]
[Column(TypeName = "geometry")]
public Geometry GeoPoly { get; set; }
  • 方案2:全局映射Geometry类型,跳过递归生成
    如果项目里有大量Geometry类型属性,不想一个个加特性,可以直接在Swagger配置里显式映射Geometry类型,告诉生成器不要递归解析它的内部结构:
services.AddSwaggerGen(c =>
{
    // 其他原有配置保留
    // 直接将Geometry类型映射为string类型Schema,不递归遍历其属性
    c.MapType<Geometry>(() => new OpenApiSchema { Type = "string", Description = "空间几何数据" });
    // 常用NetTopologySuite几何类型都可以统一映射
    c.MapType<NetTopologySuite.Geometries.Point>(() => new OpenApiSchema { Type = "string" });
    c.MapType<NetTopologySuite.Geometries.Polygon>(() => new OpenApiSchema { Type = "string" });
});
  • 方案3:配置子类型扫描规则,全局跳过NetTopologySuite类型
    如果要彻底屏蔽整个NetTopologySuite命名空间下的类型扫描,可以自定义子类型选择逻辑,遇到该命名空间下的类型直接返回空,不做递归解析:
services.AddSwaggerGen(c =>
{
    // 其他原有配置保留
    c.SelectSubTypesUsing(type =>
    {
        // 跳过NetTopologySuite命名空间下所有类型的子类型扫描
        if (type.Namespace?.StartsWith("NetTopologySuite") == true)
            return Enumerable.Empty<Type>();
        // 原有默认扫描逻辑
        return type.GetTypeInfo().Assembly.GetTypes()
            .Where(t => type.IsAssignableFrom(t) && t.IsPublic && !t.IsAbstract);
    });
});

以上任意一种方案配置完成后,可以直接删掉之前写的RemoveBogusDefinitionsDocumentFilter,启动后Swagger加载内存占用会从GB级降到正常水平,不会再出现OutOfMemoryException。

内容的提问来源于stack exchange,提问作者moodsharkh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.02 02:09:39