从模型validation中排除类型(示例DbGeography)以避免InsufficientExecutionStackException

更新:对于tl; dr版本跳到底部


我有一个非常简单的JsonConverter子类,我正在使用Web API:

public class DbGeographyJsonConverter : JsonConverter { public override bool CanConvert(Type type) { return typeof(DbGeography).IsAssignableFrom(type); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var value = (string)reader.Value; if (value.StartsWith("POINT", StringComparison.OrdinalIgnoreCase)) { return DbGeography.PointFromText(value, DbGeography.DefaultCoordinateSystemId); } else if (value.StartsWith("POLYGON", StringComparison.OrdinalIgnoreCase)) { return DbGeography.FromText(value, DbGeography.DefaultCoordinateSystemId); } else //We don't want to support anything else right now. { throw new ArgumentException(); } } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { serializer.Serialize(writer, ((DbGeography)value).AsText()); } } 

问题是,在ReadJson返回后,应用程序永远不会将绑定对象返回到action方法,因为它似乎停留在无限的validation循环中。

当我暂停执行时,这是调用堆栈的顶部:

System.Web.Http.dll!System.Web.Http.Metadata.Providers.AssociatedMetadataProvider.GetMetadataForPropertiesImpl.AnonymousMethod__0()第40行C#System.Web.Http.dll!System.Web.Http.Metadata.ModelMetadata.Model.get( )第85行C#System.Web.Http.dll!System.Web.Http.Validation.DefaultBodyModelValidator.ValidateNodeAndChildren(System.Web.Http.Metadata.ModelMetadata metadata,System.Web.Http.Validation.DefaultBodyModelValidator.ValidationContext validationContext,object container )第94行C#System.Web.Http.dll!System.Web.Http.Validation.DefaultBodyModelValidator.ValidateProperties(System.Web.Http.Metadata.ModelMetadata metadata,System.Web.Http.Validation.DefaultBodyModelValidator.ValidationContext validationContext)第156行C#System.Web.Http.dll!System.Web.Http.Validation.DefaultBodyModelValidator.ValidateNodeAndChildren(System.Web.Http.Metadata.ModelMetadata metadata,System.Web.Http.Validation.DefaultBodyModelValidator.ValidationContext validationContext,object container)第130行C#S ystem.Web.Http.dll!System.Web.Http.Validation.DefaultBodyModelValidator.ValidateElements(System.Collections.IEnumerable model,System.Web.Http.Validation.DefaultBodyModelValidator.ValidationContext validationContext)第176行C#

之后,DefaultBodyModelValidator.Validation *调用模式一遍又一遍地重复。 每次我暂停执行时,它似乎都在大约相同的深度,所以它似乎没有递归更深。

如果我强制JsonConverter返回null ,控制返回到API控制器动作方法,我假设因为没有什么可以validation。

我没有留下脑汁来解决这个问题。 我究竟做错了什么?


更新:脑汁有点补充,我已经介绍了大部分代码,看来在validation模型时, DefaultBodyModelValidator正在钻进SqlTypesAssembly并陷入循环读取属性的某个地方。 我并不真正关心DbGeography里,因为我不希望DefaultBodyModelValidator钻进DbGeography类型实例开始。

模型validation没有理由深入到DbGeography类中。 我需要弄清楚如何获取MediaTypeFormatterCollection.IsTypeExcludedFromValidation方法为typeof(DbGeography)返回true,这将导致DefaultBodyModelValidator对任何DbGeography实例执行浅层validation。 那么现在的问题是 – 如何从模型validation中排除类型? DefaultBodyModelValidatorShouldValidateType方法标记为虚拟,但是没有一种简单的方法可以在启动时添加排除类型吗?

无论这个问题是Web API的错误还是限制,我都不知道,但这是我的解决方法:

首先,我们需要ShouldValidateType DefaultBodyModelValidator并覆盖ShouldValidateType方法。

 public class CustomBodyModelValidator : DefaultBodyModelValidator { public override bool ShouldValidateType(Type type) { return type!= typeof(DbGeography) && base.ShouldValidateType(type); } } 

现在在global.asax的Application_Start方法中添加

 GlobalConfiguration.Configuration.Services.Replace(typeof(IBodyModelValidator), new CustomBodyModelValidator()); 

就是这样。 现在将在DbGeography类型实例上执行浅层validation,并且一切都很好地绑定。

只是有完全相同的问题,但后来有一个自定义类型。 经过相当多的研究,结果certificate这个线程的知识非常符合逻辑。 自定义类有一个public readonly属性,它返回同一个类的另一个实例。 validation器会查看该类的所有属性(即使您根本不进行任何validation)并获取该值。 如果你的类返回一个同一个类的新实例,这会一次又一次地发生……看起来Geography类中的StartPoint属性有同样的问题。 https://msdn.microsoft.com/en-us/library/system.data.spatial.dbgeography.startpoint(v=vs.110).aspx

joelmdev的答案引导我朝着正确的方向发展,但是在MVC和WebApi 5.2.3中使用我的WebApi配置时,放置在Global.asax中时不会调用新的validation器。

解决方案是将它放在我的WebApiConfig.Register方法中,使用其他WebApi路由: config.Services.Replace(typeof(IBodyModelValidator), new CustomBodyModelValidator());