0
votes

I have a below model from the EF entity:

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

I would like to get a list of commodities and convert that list to JSON string using Newtonsoft. Hence, I write

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

I am getting below error:

Self referencing loop detected for property 'Commodity' with type 'System.Data.Entity.DynamicProxies.Commodity_B55D25F995ED72E0B75FED715153713965D91EB5A3BF576322FE6DEAC130C0F5'. Path '[0].CommodityVariety[0]

I know it is because of the reference to Commodity in the CommodityVariety class.

To avoid I updated JSON Serializing Setting to have ReferenceLoopHandling to Ignore like below:

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

Above settings give me

Exception of type 'System.OutOfMemoryException' was thrown.

I have tried all the possible answers from the StackOverflow.

Finally, I did

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

Now, I get the code running without any error and Commodity.CommodityVarieties is null.

I have so many foreign keys and it is really hard to map them all manually after setting ProxyCreationEnabled to false.

Is there any way to identify self-referencing before JSON serializing and make it null? Like below:

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

1 Answers

0
votes

Well.. basically, most often Database model is not suited for being serialized to JSON. That's why there's often a mapping layer that translated structures from DB into API structures.

Anyways..

Nullifying the "offending" reference is one way to solve the problem, but if your ORM (EF, NH, XPO, whatever) tracks changes, you now get a risky situation: what if someone/something commits the changes afterwards? You'll save a lots of NULLs to the DB and break your data.

The JSON serializer that you are using, most probably Newtonsoft (judging from JsonConvert class) most probably offers you a configuration switch that will decide what to do when a circular reference is found in the serialized object graph. Check the docs.

For example, if you are using Newtonsoft's, then try ReferenceLoopHandling

You can also use attributes or settings to make the serializer ignore certain properties. In the example you provided, excluding CommodityVariety.Commodity would break the reference loop and solve the problem as well. Parent class has a collection of children, children no longer care about the parent, everything's fine for JSON. However, after DEserializing, you may need to manually fix the child-to-parent back-references back.

You can also implement and add your own plugins for the serializer and completely define the serialization however you like, that a sure-fire solution, but quite an overkill for this problem here.