简体   繁体   English

序列化嵌套 Object 时的自引用

[英]Self Reference when serializing nested Object

I want to store a snapshot of a nested model in my database as sort of a change history.我想在我的数据库中存储嵌套 model 的快照作为更改历史记录。 Therefore I made a model that serializes the whole object into a JSON string for easier storage.因此,我制作了一个 model 将整个 object 序列化为 JSON 字符串以便于存储。

Simplified Data class I want to store:简化Data class 我要存储:

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

The DataObject for the collection inside Data : Data内部集合的DataObject

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

My snapshot class looks something like this:我的快照 class 看起来像这样:

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
                });
        }
    }
}

Inside my controller I do:在我的 controller 里面我做:

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

GetData() method inside the repository:存储库中的GetData()方法:

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

The problem is when I try to serialize one Data entry I get a self reference inside the DataObject so it includes the Data object again and also the DataObjects .问题是当我尝试序列化一个Data条目时,我在DataObject中获得了一个自我引用,因此它再次包含Data object 以及DataObjects Even with ReferenceLoopHandling.Ignore the produced JSON looks something like this:即使使用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
          }
        ]
      }
    }
  ]
}

EDIT: Expected output would be something like this:编辑:预计 output 将是这样的:

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

How can I stop it from including Data a second time without using DTOs?如何在不使用 DTO 的情况下阻止它再次包含Data

EDIT:编辑:

If I try it without Entity Framework, ReferenceLoopHandling.None works as expected.如果我在没有实体框架的情况下尝试它, ReferenceLoopHandling.None会按预期工作。 See Dotnet Fiddle https://dotnetfiddle.net/bmAoAW .请参阅 Dotnet Fiddle https://dotnetfiddle.net/bmAoAW So there seems to be a problem with my EF Core configuration or something.所以我的 EF Core 配置似乎有问题。

You said in the comments that effectively you want to DataObject.Data property to be ignored whenever you are serializing Data from within DataHistory .您在评论中说,当您从DataHistory中序列化Data时,您实际上希望忽略DataObject.Data属性。 You can do this by using a custom ContractResolver to ignore the property programmatically.您可以通过使用自定义ContractResolver以编程方式忽略该属性来执行此操作。

Here is the code you would need for the resolver:这是解析器所需的代码:

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;
    }
}

Then apply it within the JsonSerializerSettings in DataHistory.Data :然后在JsonSerializerSettingsDataHistory.Data中应用它:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM