PropertyType无法匹配接口及自身类型:动态装配引发的Swagger过滤问题
问题解决:Swagger SchemaFilter 无法过滤动态生成程序集中的IEntity属性
问题背景
为了在Swagger API文档中忽略所有实现IEntity接口的属性,编写了如下SchemaFilter:
Public Class cSwaggerIgnoreNavigationProperties Implements ISchemaFilter Public Sub Apply(voSchema As OpenApiSchema, voContext As SchemaFilterContext) Implements ISchemaFilter.Apply If voSchema.Properties.Count = 0 Then Return End If Const oFlags = BindingFlags.Instance Or BindingFlags.NonPublic Or BindingFlags.Public Dim oPropertyList = voContext.Type.GetProperties(oFlags) Dim oExcludedList = oPropertyList.Where(Function(p) p.PropertyType.IsAssignableTo(GetType(IEntity))) _ .Select(Function(p) Dim oCamelCaser As cCamelCaser = New cCamelCaser() Return If(p.GetCustomAttribute(Of JsonPropertyNameAttribute)()?.Name, oCamelCaser.ToLowerCamelCase(p.Name)) End Function) For Each sExcluded In oExcludedList If voSchema.Properties.ContainsKey(sExcluded) Then voSchema.Properties.Remove(sExcluded) End If Next End Sub End Class
多数场景下该过滤器工作正常,但部分类型(如S4D_Data.Patient,该类型继承自实现IEntity的基类)的属性未被正确过滤。
调试异常现象
调试时发现针对S4D_Data.Patient类型的属性,p.PropertyType.IsAssignableTo(GetType(IEntity))返回False:
?p.PropertyType.FullName "S4D_Data.Patient" ?p.PropertyType.IsAssignableTo(GetType(S4D_Data.IEntity)) False
但直接检查类型本身时结果正常:
?GetType(S4D_Data.Patient).IsAssignableTo(GetType(S4D_Data.IEntity)) True
更异常的是,反射得到的PropertyType实例无法匹配自身类型:
?p.PropertyType.IsAssignableTo(GetType(S4D_Data.Patient)) False ?p.PropertyType = GetType(S4D_Data.Patient) False
问题根源
进一步排查确认,系统加载了两个包含S4D_Data.Patient类型的程序集:
- 原始程序集:
S4D_Data.Patient, S4D Data, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null - 动态生成程序集:
S4D_Data.Patient, V2e56f2547b0f45d2a33a0d70b908a55e.DynamicModels, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
反射获取的属性类型来自动态生成程序集,而GetType(IEntity)指向的是原始程序集中的接口,两者属于不同程序集实例,导致类型匹配逻辑失效。
解决方案
修改类型判断逻辑,通过类型全名+接口全名进行匹配,摆脱对程序集实例的依赖:
Public Class cSwaggerIgnoreNavigationProperties Implements ISchemaFilter Public Sub Apply(voSchema As OpenApiSchema, voContext As SchemaFilterContext) Implements ISchemaFilter.Apply If voSchema.Properties.Count = 0 Then Return End If Const oFlags = BindingFlags.Instance Or BindingFlags.NonPublic Or BindingFlags.Public ' 获取目标接口的全名(包含命名空间) Dim targetInterfaceFullName = GetType(IEntity).FullName Dim oPropertyList = voContext.Type.GetProperties(oFlags) Dim oExcludedList = oPropertyList.Where(Function(p) ' 遍历属性类型实现的所有接口,通过全名匹配目标接口 Return p.PropertyType.GetInterfaces().Any(Function(i) i.FullName = targetInterfaceFullName) ) _ .Select(Function(p) Dim oCamelCaser As cCamelCaser = New cCamelCaser() Return If(p.GetCustomAttribute(Of JsonPropertyNameAttribute)()?.Name, oCamelCaser.ToLowerCamelCase(p.Name)) End Function) For Each sExcluded In oExcludedList If voSchema.Properties.ContainsKey(sExcluded) Then voSchema.Properties.Remove(sExcluded) End If Next End Sub End Class
逻辑说明
- 先获取
IEntity接口的完整名称(包含命名空间),作为匹配基准 - 对每个属性类型,遍历其实现的所有接口,通过接口全名判断是否符合过滤条件
- 该方式不依赖程序集加载实例,只要类型实现了同全名的接口就能被正确识别
内容的提问来源于stack exchange,提问作者Daniel Vernall
相关产品推荐
相关产品推荐

