JsonMediaTypeFormatter正在尝试反序列化具有IgnoreDataMember属性的属性

JsonMediaTypeFormatter正在尝试反序列化具有IgnoreDataMember属性的属性,json,deserialization,asp.net-web-api,circular-reference,Json,Deserialization,Asp.net Web Api,Circular Reference,我有一个JSON字符串,例如: { "$id": "1", "Username": "mrdan", "Email": "mrdan@hotmale.co.uk", "Roles": [ { "$id": "2", "Name": "Super Admin", "Users": [ { "$ref": "1" } ], "Permissions": [

我有一个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字符串反序列化到一个用户对象中,并且所有的循环引用都完好无损,那么为什么它不为我所做的操作这么做呢

如果您需要更多信息,请告诉我,这确实让我很沮丧,因为我无法调试它:(

编辑 我正在反序列化到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);
        }
    }

}
    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);
    }
[DataContract]
公共类用户:ServerEntityBase
{
[数据成员]
公共字符串用户名{get;set;}
[数据成员]
公共字符串电子邮件{get;set;}
[数据成员]
[相关]
公共ICollection角色{get;set;}
[IgnoreDataMember]
公共字符串密码哈希{get;set;}
[IgnoreDataMember]
公共重写表达式SavingObjectGraph
{
收到
{
返回map=>map.AssociatedCollection(u=>u.Roles);
}
}
}
奇怪的是,在反序列化过程中,实际上调用了SavingObjectGraph属性的Get。我不知道调用它的是什么,我希望它不是。在调试过程中,如果跳过Get调用,它实际上会成功反序列化,并以用户作为参数输入我的保存操作

你知道我怎样才能停止对Get的呼叫吗?更好的是,是什么让它一开始就被呼叫

编辑2 下面是调用Get时stacktrace的顶部,现在的问题是如何从“元数据提供程序”忽略此属性:)到达那里

   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()
在UKStainedGlass.Business.Entities.User.get_SavingObjectGraph()上
在GetSavingObjectGraphFromUser处(对象)
位于System.Web.Http.Metadata.Providers.AssociatedMetadataProvider`1.c__DisplayClass3.b_u 0()
在System.Web.Http.Metadata.ModelMetadata.get_Model()上
位于System.Web.Http.Validation.DefaultBodyModelValidator.ValidateNodeAndChildren(ModelMetadata元数据、ValidationContext ValidationContext、对象容器、IEnumerable`1验证器)
位于System.Web.Http.Validation.DefaultBodyModelValidator.ValidateProperties(ModelMetadata元数据,ValidationContext ValidationContext)
位于System.Web.Http.Validation.DefaultBodyModelValidator.ValidateNodeAndChildren(ModelMetadata元数据、ValidationContext ValidationContext、对象容器、IEnumerable`1验证器)
位于System.Web.Http.Validation.DefaultBodyModelValidator.Validate(对象模型、类型、ModelMetadataProvider metadataProvider、HttpActionContext actionContext、String keyPrefix)
在System.Web.Http.ModelBinding.FormatterParameterBinding.d__0.MoveNext()中
在System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine&stateMachine)
在System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Start[TStateMachine](TStateMachine&stateMachine)中
位于System.Web.Http.ModelBinding.FormatterParameterBinding.ExecuteBindingAsyncCore(ModelMetadataProvider metadataProvider、HttpActionContext actionContext、HttpParameterDescriptor paramFromBody、类型、HttpRequestMessage请求、IFormatterLogger formatterLogger、CancellationToken CancellationToken)
位于System.Web.Http.ModelBinding.FormatterParameterBinding.ExecuteBindingAsync(ModelMetadataProvider metadataProvider,HttpActionContext actionContext,CancellationToken CancellationToken)
在System.Web.Http.Controllers.HttpActionBinding.d_u0.MoveNext()中
在System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine&stateMachine)
在System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Start[TStateMachine](TStateMachine&stateMachine)中
位于System.Web.Http.Controller.HttpActionBinding.ExecuteBindingAsyncCore(HttpActionContext actionContext,CancellationToken CancellationToken)
在System.Web.Http.Controllers.HttpActionBinding.ExecuteBindingAsync(HttpActionContext actionContext,CancellationToken CancellationToken)
在System.Web.Http.Controllers.ActionFilterResult.d_u2.MoveNext()中

我终于解决了这个问题

原来线索在堆栈跟踪中。WebAPI中有一个用于模型绑定的验证过程,在该过程中的某个地方有一个调用,名为
ShouldValidateType(Type)
。对于我的SavingObjectGraph属性,该函数返回true,这反过来导致用户到角色到用户的无限循环,等等

为了解决这个问题,我必须更改两个位置,首先,对
类DefaultBodyModelValidator
进行子类化,覆盖其函数
公共覆盖bool ShouldValidateType(Type Type)
,以便在查看我的SavingObjectGraph属性时返回false。像这样:

public class CustomBodyModelValidator : DefaultBodyModelValidator
{
    public override bool ShouldValidateType(Type type)
    {
        if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Expression<>))
        {
            return false;
        }
        return base.ShouldValidateType(type);
    }
}
问题解决了!恐怕我已经忘记在哪里找到答案了:(但事实证明,读取堆栈跟踪可以有所帮助;)