JsonMediaTypeFormatter 尝试使用 IgnoreDataMember 属性反序列化 属性
JsonMediaTypeFormatter trying to deserialise a property with IgnoreDataMember attribute
我有一个 JSON 字符串,例如:
{
"$id": "1",
"Username": "mrdan",
"Email": "mrdan@hotmale.co.uk",
"Roles": [
{
"$id": "2",
"Name": "Super Admin",
"Users": [
{
"$ref": "1"
}
],
"Permissions": [
{
"$id": "3",
"Name": "UserSave",
"Roles": [
{
"$ref": "2"
}
],
"Id": "2d9a1268-6e53-4749-89f6-59ec0132e737"
},
{
"$id": "4",
"Name": "UserView",
"Roles": [
{
"$ref": "2"
},
{
"$id": "5",
"Name": "Call Centre Manager",
"Users": [
{
"$id": "6",
"Username": "mrdan2",
"Email": "mrdan2@hotmale.co.uk",
"Roles": [
{
"$ref": "5"
}
],
"Id": "579a0c65-26f6-4be5-aa78-72e1cd76ba11"
}
],
"Permissions": [
{
"$ref": "4"
}
],
"Id": "f44702ef-03b0-4694-afcf-dc79c6826938"
}
],
"Id": "69c9a26b-0524-4b71-9675-5f167d2a9afc"
},
{
"$id": "7",
"Name": "UserDelete",
"Roles": [
{
"$ref": "2"
}
],
"Id": "f4310d1e-1888-4917-a1de-e3f63f77a88a"
}
],
"Id": "c5f10adc-1d46-424e-afab-584cc0a8375c"
}
],
"Id": "45fa847e-ceae-4fbb-b1cc-a42dfeb53c72"
}
... 我想发送到 WebAPI 操作。 JSON 是我用所有多汁的循环引用加载用户后得到的,我有:
config.Formatters.Add(
new JsonMediaTypeFormatter()
{
SerializerSettings = new Newtonsoft.Json.JsonSerializerSettings()
{
PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects,
ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Serialize
}
}
);
... 在我的 WebAPI 启动中,这在向下到客户端的路上运行良好。 (我还没有在客户端测试它,我目前正在使用 RESTClient for Firefox 进行测试。)
现在,我遇到的问题是当我将该数据发送回服务器时。我的控制器功能是:
[Route("api/User")]
[HttpPost]
[OperationBehavior(TransactionScopeRequired = true)]
public User Save([FromBody]User user)
现在,当我发送一个没有任何循环引用的用户时,一切正常,当我发送任何没有循环引用的东西时,一切正常。
症状是在反序列化的时候挂了。我已经测试过自定义 httphandler 能够将它从 json 字符串反序列化为 User 对象,并且所有循环引用都完好无损,那么为什么它不为我的操作做呢?
如果您需要更多信息,请告诉我,这让我很沮丧,因为我无法调试它:(
编辑
我正在像这样反序列化为 POCO:
[DataContract]
public class User : ServerEntityBase<User>
{
[DataMember]
public string Username { get; set; }
[DataMember]
public string Email { get; set; }
[DataMember]
[Associated]
public ICollection<Role> Roles { get; set; }
[IgnoreDataMember]
public string PasswordHash { get; set; }
[IgnoreDataMember]
public override Expression<Func<IUpdateConfiguration<User>, object>> SavingObjectGraph
{
get
{
return map => map.AssociatedCollection(u => u.Roles);
}
}
}
奇怪的是在反序列化过程中实际上调用了 SavingObjectGraph 属性 的 Get。我不知道它叫什么,我希望它不是。在调试过程中,如果我跳过 Get 调用,它实际上会成功反序列化并以用户作为参数进入我的保存操作。
知道如何停止对 Get 的调用吗?更好的是,是什么导致它被调用开始?
编辑 2
这是调用 Get 时堆栈跟踪的顶部部分,现在的问题是如何从 "metadata provider" 中忽略此 属性 :) 到达那里!
at UKStainedGlass.Business.Entities.User.get_SavingObjectGraph()
at GetSavingObjectGraphFromUser(Object )
at System.Web.Http.Metadata.Providers.AssociatedMetadataProvider`1.<>c__DisplayClass3.<GetMetadataForPropertiesImpl>b__0()
at System.Web.Http.Metadata.ModelMetadata.get_Model()
at System.Web.Http.Validation.DefaultBodyModelValidator.ValidateNodeAndChildren(ModelMetadata metadata, ValidationContext validationContext, Object container, IEnumerable`1 validators)
at System.Web.Http.Validation.DefaultBodyModelValidator.ValidateProperties(ModelMetadata metadata, ValidationContext validationContext)
at System.Web.Http.Validation.DefaultBodyModelValidator.ValidateNodeAndChildren(ModelMetadata metadata, ValidationContext validationContext, Object container, IEnumerable`1 validators)
at System.Web.Http.Validation.DefaultBodyModelValidator.Validate(Object model, Type type, ModelMetadataProvider metadataProvider, HttpActionContext actionContext, String keyPrefix)
at System.Web.Http.ModelBinding.FormatterParameterBinding.<ExecuteBindingAsyncCore>d__0.MoveNext()
at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine)
at System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Start[TStateMachine](TStateMachine& stateMachine)
at System.Web.Http.ModelBinding.FormatterParameterBinding.ExecuteBindingAsyncCore(ModelMetadataProvider metadataProvider, HttpActionContext actionContext, HttpParameterDescriptor paramFromBody, Type type, HttpRequestMessage request, IFormatterLogger formatterLogger, CancellationToken cancellationToken)
at System.Web.Http.ModelBinding.FormatterParameterBinding.ExecuteBindingAsync(ModelMetadataProvider metadataProvider, HttpActionContext actionContext, CancellationToken cancellationToken)
at System.Web.Http.Controllers.HttpActionBinding.<ExecuteBindingAsyncCore>d__0.MoveNext()
at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine)
at System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Start[TStateMachine](TStateMachine& stateMachine)
at System.Web.Http.Controllers.HttpActionBinding.ExecuteBindingAsyncCore(HttpActionContext actionContext, CancellationToken cancellationToken)
at System.Web.Http.Controllers.HttpActionBinding.ExecuteBindingAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
at System.Web.Http.Controllers.ActionFilterResult.<ExecuteAsync>d__2.MoveNext()
好吧,我终于解决了这个问题!
原来线索在堆栈跟踪中。在 WebAPI 中有一个用于模型绑定的验证过程,在该过程中的某处有一个对名为 ShouldValidateType(Type type)
的调用。对于我的 SavingObjectGraph 属性,该函数 return 为真,这反过来又导致了用户到角色到用户的无限循环,等等。
我不得不改变两个地方来解决这个问题,首先,子类化 class DefaultBodyModelValidator
,在查看我的 SavingObjectGraph [=26] 时将其函数 public override bool ShouldValidateType(Type type)
重写为 return false =].像这样:
public class CustomBodyModelValidator : DefaultBodyModelValidator
{
public override bool ShouldValidateType(Type type)
{
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Expression<>))
{
return false;
}
return base.ShouldValidateType(type);
}
}
然后,在我的 WebAPI 的服务上注册它,如下所示:
public void Configuration(IAppBuilder appBuilder)
{
// Configure Web API for self-host.
HttpConfiguration config = new HttpConfiguration();
Register(config);
config.Services.Replace(typeof(IBodyModelValidator), new CustomBodyModelValidator());
appBuilder.UseWebApi(config);
}
问题解决了!恐怕我忘记了在哪里找到答案:(但事实证明阅读堆栈跟踪可以帮助 ;)
我有一个 JSON 字符串,例如:
{
"$id": "1",
"Username": "mrdan",
"Email": "mrdan@hotmale.co.uk",
"Roles": [
{
"$id": "2",
"Name": "Super Admin",
"Users": [
{
"$ref": "1"
}
],
"Permissions": [
{
"$id": "3",
"Name": "UserSave",
"Roles": [
{
"$ref": "2"
}
],
"Id": "2d9a1268-6e53-4749-89f6-59ec0132e737"
},
{
"$id": "4",
"Name": "UserView",
"Roles": [
{
"$ref": "2"
},
{
"$id": "5",
"Name": "Call Centre Manager",
"Users": [
{
"$id": "6",
"Username": "mrdan2",
"Email": "mrdan2@hotmale.co.uk",
"Roles": [
{
"$ref": "5"
}
],
"Id": "579a0c65-26f6-4be5-aa78-72e1cd76ba11"
}
],
"Permissions": [
{
"$ref": "4"
}
],
"Id": "f44702ef-03b0-4694-afcf-dc79c6826938"
}
],
"Id": "69c9a26b-0524-4b71-9675-5f167d2a9afc"
},
{
"$id": "7",
"Name": "UserDelete",
"Roles": [
{
"$ref": "2"
}
],
"Id": "f4310d1e-1888-4917-a1de-e3f63f77a88a"
}
],
"Id": "c5f10adc-1d46-424e-afab-584cc0a8375c"
}
],
"Id": "45fa847e-ceae-4fbb-b1cc-a42dfeb53c72"
}
... 我想发送到 WebAPI 操作。 JSON 是我用所有多汁的循环引用加载用户后得到的,我有:
config.Formatters.Add(
new JsonMediaTypeFormatter()
{
SerializerSettings = new Newtonsoft.Json.JsonSerializerSettings()
{
PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects,
ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Serialize
}
}
);
... 在我的 WebAPI 启动中,这在向下到客户端的路上运行良好。 (我还没有在客户端测试它,我目前正在使用 RESTClient for Firefox 进行测试。)
现在,我遇到的问题是当我将该数据发送回服务器时。我的控制器功能是:
[Route("api/User")]
[HttpPost]
[OperationBehavior(TransactionScopeRequired = true)]
public User Save([FromBody]User user)
现在,当我发送一个没有任何循环引用的用户时,一切正常,当我发送任何没有循环引用的东西时,一切正常。
症状是在反序列化的时候挂了。我已经测试过自定义 httphandler 能够将它从 json 字符串反序列化为 User 对象,并且所有循环引用都完好无损,那么为什么它不为我的操作做呢?
如果您需要更多信息,请告诉我,这让我很沮丧,因为我无法调试它:(
编辑
我正在像这样反序列化为 POCO:
[DataContract]
public class User : ServerEntityBase<User>
{
[DataMember]
public string Username { get; set; }
[DataMember]
public string Email { get; set; }
[DataMember]
[Associated]
public ICollection<Role> Roles { get; set; }
[IgnoreDataMember]
public string PasswordHash { get; set; }
[IgnoreDataMember]
public override Expression<Func<IUpdateConfiguration<User>, object>> SavingObjectGraph
{
get
{
return map => map.AssociatedCollection(u => u.Roles);
}
}
}
奇怪的是在反序列化过程中实际上调用了 SavingObjectGraph 属性 的 Get。我不知道它叫什么,我希望它不是。在调试过程中,如果我跳过 Get 调用,它实际上会成功反序列化并以用户作为参数进入我的保存操作。
知道如何停止对 Get 的调用吗?更好的是,是什么导致它被调用开始?
编辑 2
这是调用 Get 时堆栈跟踪的顶部部分,现在的问题是如何从 "metadata provider" 中忽略此 属性 :) 到达那里!
at UKStainedGlass.Business.Entities.User.get_SavingObjectGraph()
at GetSavingObjectGraphFromUser(Object )
at System.Web.Http.Metadata.Providers.AssociatedMetadataProvider`1.<>c__DisplayClass3.<GetMetadataForPropertiesImpl>b__0()
at System.Web.Http.Metadata.ModelMetadata.get_Model()
at System.Web.Http.Validation.DefaultBodyModelValidator.ValidateNodeAndChildren(ModelMetadata metadata, ValidationContext validationContext, Object container, IEnumerable`1 validators)
at System.Web.Http.Validation.DefaultBodyModelValidator.ValidateProperties(ModelMetadata metadata, ValidationContext validationContext)
at System.Web.Http.Validation.DefaultBodyModelValidator.ValidateNodeAndChildren(ModelMetadata metadata, ValidationContext validationContext, Object container, IEnumerable`1 validators)
at System.Web.Http.Validation.DefaultBodyModelValidator.Validate(Object model, Type type, ModelMetadataProvider metadataProvider, HttpActionContext actionContext, String keyPrefix)
at System.Web.Http.ModelBinding.FormatterParameterBinding.<ExecuteBindingAsyncCore>d__0.MoveNext()
at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine)
at System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Start[TStateMachine](TStateMachine& stateMachine)
at System.Web.Http.ModelBinding.FormatterParameterBinding.ExecuteBindingAsyncCore(ModelMetadataProvider metadataProvider, HttpActionContext actionContext, HttpParameterDescriptor paramFromBody, Type type, HttpRequestMessage request, IFormatterLogger formatterLogger, CancellationToken cancellationToken)
at System.Web.Http.ModelBinding.FormatterParameterBinding.ExecuteBindingAsync(ModelMetadataProvider metadataProvider, HttpActionContext actionContext, CancellationToken cancellationToken)
at System.Web.Http.Controllers.HttpActionBinding.<ExecuteBindingAsyncCore>d__0.MoveNext()
at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine)
at System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Start[TStateMachine](TStateMachine& stateMachine)
at System.Web.Http.Controllers.HttpActionBinding.ExecuteBindingAsyncCore(HttpActionContext actionContext, CancellationToken cancellationToken)
at System.Web.Http.Controllers.HttpActionBinding.ExecuteBindingAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
at System.Web.Http.Controllers.ActionFilterResult.<ExecuteAsync>d__2.MoveNext()
好吧,我终于解决了这个问题!
原来线索在堆栈跟踪中。在 WebAPI 中有一个用于模型绑定的验证过程,在该过程中的某处有一个对名为 ShouldValidateType(Type type)
的调用。对于我的 SavingObjectGraph 属性,该函数 return 为真,这反过来又导致了用户到角色到用户的无限循环,等等。
我不得不改变两个地方来解决这个问题,首先,子类化 class DefaultBodyModelValidator
,在查看我的 SavingObjectGraph [=26] 时将其函数 public override bool ShouldValidateType(Type type)
重写为 return false =].像这样:
public class CustomBodyModelValidator : DefaultBodyModelValidator
{
public override bool ShouldValidateType(Type type)
{
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Expression<>))
{
return false;
}
return base.ShouldValidateType(type);
}
}
然后,在我的 WebAPI 的服务上注册它,如下所示:
public void Configuration(IAppBuilder appBuilder)
{
// Configure Web API for self-host.
HttpConfiguration config = new HttpConfiguration();
Register(config);
config.Services.Replace(typeof(IBodyModelValidator), new CustomBodyModelValidator());
appBuilder.UseWebApi(config);
}
问题解决了!恐怕我忘记了在哪里找到答案:(但事实证明阅读堆栈跟踪可以帮助 ;)