c# - 序列化嵌套对象时的自引用
问题描述
我想在我的数据库中存储嵌套模型的快照作为更改历史记录。因此,我制作了一个模型,将整个对象序列化为 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 配置似乎有问题。
解决方案
您在评论中说,当您从内部DataObject.Data
序列化时,您实际上希望属性被忽略。您可以通过使用自定义以编程方式忽略该属性来执行此操作。Data
DataHistory
ContractResolver
这是解析器所需的代码:
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;
}
}
然后在JsonSerializerSettings
in 中应用它DataHistory.Data
:
set { _Data = JsonConvert.SerializeObject(value , Formatting.Indented,
new JsonSerializerSettings {
ContractResolver = new CustomResolver(),
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
PreserveReferencesHandling = PreserveReferencesHandling.None
});
}
推荐阅读
- java - 如何使用命令创建java项目?
- css - 是否有用于将 css flex 简写转换为简写以支持 IE11 的 polyfill?
- firebase - Firebase 分析事件参数未显示在控制台中
- c# - 将null映射到[NotNull]时如何使AutoMapper崩溃?
- java - 检索包含逗号 Apache DB 的 VARCHAR 数据
- javascript - 如何在 Parse SDK 中运行复杂查询
- javascript - Firebase 错误每个 then() 都应该返回一个值
- html - 如何让 Nuxt.js 预渲染页面的完整 HTML?
- .net - 是为每个项目安装一个 nuget 包,还是为整个解决方案全局安装更好?
- amazon-web-services - 如何检测 S3 中未映射到我们数据库的孤立对象?