【问题标题】:How to identify self referencing loop?如何识别自引用循环?
【发布时间】:2023-04-01 11:04:02
【问题描述】:

我有一个来自 EF 实体的以下模型:

public partial class Commodity
{
    public Commodity()
    {
        this.CommodityVarieties = new HashSet<CommodityVariety>();
    }

    public int CommodityID { get; set; }
    public string CommodityName { get; set; }
    public string CommodityVarietyDisplayName { get; set; }
    public string BackColor { get; set; }
    public string ForeColor { get; set; }
    public bool IsDeleted { get; set; }
    public int SortOrder { get; set; }

    public virtual ICollection<CommodityVariety> CommodityVarieties { get; set; }

}

public partial class CommodityVariety
{
    public int VarietyID { get; set; }
    public int CommodityID { get; set; }
    public string VarietyName { get; set; }
    public bool IsDeleted { get; set; }

    public virtual Commodity Commodity { get; set; }
}

我想获取商品列表并使用Newtonsoft 将该列表转换为 JSON 字符串。因此,我写了

DbContext context = new DbContext();
var list = context.Commodities.ToList();
string json = JsonConvert.SerializeObject(list);

我遇到以下错误:

检测到具有类型的属性“商品”的自引用循环 'System.Data.Entity.DynamicProxies.Commodity_B55D25F995ED72E0B75FED715153713965D91EB5A3BF576322FE6DEAC130C0F5'。 路径'[0].CommodityVariety[0]

我知道这是因为 CommodityVariety 类中对 Commodity 的引用。

为避免我将 JSON 序列化设置更新为将 ReferenceLoopHandling 改为 Ignore,如下所示:

options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;

以上设置给我

引发了“System.OutOfMemoryException”类型的异常。

我已经尝试了 StackOverflow 中所有可能的答案。

我终于做到了

public DbContext() : base("name=DbContextEntity")
{
    Configuration.LazyLoadingEnabled = false;
    Configuration.ProxyCreationEnabled = false;
}

现在,我运行代码没有任何错误,Commodity.CommodityVarieties 为空。

我有这么多外键,在将ProxyCreationEnabled 设置为false 后,很难手动映射它们。

有没有办法在 JSON 序列化之前识别自引用并使其为空?如下:

DbContext context = new DbContext();
var list = context.Commodities.ToList();

//filter only properties of `Commodity` property and find object of type `Commodity`
//var suspectObjects = list.Any(x => x.OfType<Commodity>()).ToList();
//suspectObjects.ForEach(item => { item = null; });

//I know the above segment will not work. I seek your help and I thought something like above explain more to you what I want actually.

string json = JsonConvert.SerializeObject(list);

【问题讨论】:

    标签: c# json json.net


    【解决方案1】:

    嗯.. 基本上,数据库模型通常不适合序列化为 JSON。这就是为什么通常会有一个映射层将结构从 DB 转换为 API 结构。

    无论如何..

    取消“违规”引用是解决问题的一种方法,但如果您的 ORM(EF、NH、XPO 等)跟踪更改,您现在会遇到危险情况:如果某人/某事提交 之后的变化?您将在数据库中保存大量 NULL 并破坏您的数据。

    您正在使用的 JSON 序列化程序,很可能是 Newtonsoft(从 JsonConvert 类判断)最可能为您提供了一个配置开关,它将决定在序列化对象图中找到循环引用时要做什么。检查文档。

    例如,如果您使用的是 Newtonsoft 的,请尝试 ReferenceLoopHandling

    您还可以使用属性或设置使序列化程序忽略某些属性。在您提供的示例中,排除 CommodityVariety.Commodity 会破坏参考循环并解决问题。父类有一个孩子的集合,孩子不再关心父母,JSON 一切都很好。但是,在反序列化之后,您可能需要手动修复子到父的反向引用。

    您还可以为序列化程序实现和添加自己的插件,并根据您的喜好完全定义序列化,这是一个可靠的解决方案,但对于这个问题来说,这有点矫枉过正。

    【讨论】:

    • 感谢您的回答。我尝试使用ReferenceLoopHandling.Ignore,因为我有我的属性,我最终得到了'System.OutOfMemoryException'。
    • 我想找到所有引用父类(在我的例子中是“商品”)的属性(包括子属性),并在序列化之前使其为空。我坚信这样我可以摆脱自引用循环错误和内存不足异常。
    • 大部分情况下,我的数据库对象是稳定的并且不经常更新。因此,我选择在序列化之前取消自引用。
    • 因此,我的问题是如何取消“违规”引用?
    • @MohamedThaufeeq:不幸的是,我现在没时间了。请接受我的建议:如果您只想序列化该数据并且除了将引用归零之外没有任何其他想法,那么请坚持使用ReferenceLoopHandling.Ignore。它有效地做你想做的事:它使已经在对象树中看到的任何引用无效。如果在您使用该选项时抛出 OutOfMemory,那么它很可能会与您的自定义 null'ifying 一起被抛出。由于某些其他原因,很可能会引发 OOM。使用 Ignore 并首先专注于诊断 OutOfMemory。
    猜你喜欢
    • 2015-11-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多