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
相关产品推荐
相关产品推荐

