首页 > 解决方案 > 序列化嵌套对象时的自引用

问题描述

我想在我的数据库中存储嵌套模型的快照作为更改历史记录。因此,我制作了一个模型,将整个对象序列化为 JSON 字符串,以便于存储。

Data我要存储的简化类:

public class Data
{
    public int Id { get; set; }
    public string SomeInfo { get; set; }
    public virtual ICollection<DataObject> DataObject { get; set; }
}

对于DataObject里面的集合Data

public class DataObject
{
    public int Id { get; set; }
    public string SomeMoreInfo { get; set; }
    public int DataId { get; set; }
    public virtual Data Data { get; set; }
}

我的快照类看起来像这样:

public class DataHistory
{
    public int Id { get; set; }
    private string _Data;

    [NotMapped]
    public Data Data
    {
        get { return _Data == null ? null : JsonConvert.DeserializeObject<Data>(_Data); }
        set { _Data = JsonConvert.SerializeObject(value , Formatting.Indented, 
                new JsonSerializerSettings { 
                        ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
                        PreserveReferencesHandling = PreserveReferencesHandling.None
                });
        }
    }
}

在我的控制器里面我做:

var data = await _repo.GetData(id);
var historyEntry = new DataHistory();
historyEntry.Data= data;
_repo.Add(historyEntry); 

GetData()存储库中的方法:

public async Task<Data> GetData(int id)
{
   return await _context.Data
   .Include(d => d.DataObject)
   .FirstOrDefaultAsync(d => d.Id == id);
}

问题是当我尝试序列化一个Data条目时,我在内部得到一个自我引用,DataObject因此它Data再次包含对象以及DataObjects. 即使ReferenceLoopHandling.Ignore 生成的 JSON 看起来像这样:

{
  "Id": 1051,
  "SomeInfo": "asdasd",
  "DataObject": [
    {
      "Id": 121,
      "SomeMoreInfo": "asdasd",
      "Data": {
        "Id": 1051,
        "SomeInfo": "asdasd",
        "DataObject": [
          {
            "Id": 122,
            "SomeMoreInfo": "asdasd",
            "DataId": 1051
          }
        ]
      }
    },
    {
      "Id": 122,
      "SomeMoreInfo": "asdasd",
      "Data": {
        "Id": 1051,
        "SomeInfo": "asdasd",
        "DataObject": [
          {
            "Id": 121,
            "SomeMoreInfo": "asdasd",
            "DataId": 1051
          }
        ]
      }
    }
  ]
}

编辑:预期的输出将是这样的:

{
    "Id": 1051,
    "SomeInfo": "Data",
    "DataObject": [
        {
            "Id": 121,
            "SomeMoreInfo": "DataObject1"
            "DataId": 1051
        },
        {
            "Id": 122,
            "SomeMoreInfo": "DataObject2"
            "DataId": 1051
        }
    ]
}

如何在Data不使用 DTO 的情况下阻止它包含第二次?

编辑:

如果我在没有实体框架的情况下尝试它,则ReferenceLoopHandling.None可以按预期工作。请参阅 Dotnet Fiddle https://dotnetfiddle.net/bmAoAW。所以我的 EF Core 配置似乎有问题。

标签: c#.net-corejson.netentity-framework-core

解决方案


您在评论中说,当您从内部DataObject.Data序列化时,您实际上希望属性被忽略。您可以通过使用自定义以编程方式忽略该属性来执行此操作。DataDataHistoryContractResolver

这是解析器所需的代码:

public class CustomResolver : DefaultContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        JsonProperty prop = base.CreateProperty(member, memberSerialization);
        if (prop.DeclaringType == typeof(DataObject) && prop.PropertyName == nameof(DataObject.Data))
        {
            prop.Ignored = true;
        }
        return prop;
    }
}

然后在JsonSerializerSettingsin 中应用它DataHistory.Data

    set { _Data = JsonConvert.SerializeObject(value , Formatting.Indented, 
            new JsonSerializerSettings { 
                    ContractResolver = new CustomResolver(),
                    ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
                    PreserveReferencesHandling = PreserveReferencesHandling.None
            });
    }

推荐阅读