12

I have been experimenting with creating a website that leverages MVC with JSON for my presentation layer and Entity framework for data model/database. My Issue comes into play with serializing my Model objects into JSON.

I am using the code first method to create my database. When doing the code first method a one to many relationship (parent/child) requires the child to have a reference back to the parent. (Example code my be a typo but you get the picture)

class parent
{
   public List<child> Children{get;set;}
   public int Id{get;set;}

}
class child
{
    public int ParentId{get;set;}
    [ForeignKey("ParentId")]
    public parent MyParent{get;set;}
    public string name{get;set;}
 }

When returning a "parent" object via a JsonResult a circular reference error is thrown because "child" has a property of class parent.

I have tried the ScriptIgnore attribute but I lose the ability to look at the child objects. I will need to display information in a parent child view at some point.

I have tried to make base classes for both parent and child that do not have a circular reference. Unfortunately when I attempt to send the baseParent and baseChild these are read by the JSON Parser as their derived classes (I am pretty sure this concept is escaping me).

Base.baseParent basep = (Base.baseParent)parent;
return Json(basep, JsonRequestBehavior.AllowGet);

The one solution I have come up with is to create "View" Models. I create simple versions of the database models that do not include the reference to the parent class. These view models each have method to return the Database Version and a constructor that takes the database model as a parameter (viewmodel.name = databasemodel.name). This method seems forced although it works.

NOTE:I am posting here because I think this is more discussion worthy. I could leverage a different design pattern to over come this issue or it could be as simple as using a different attribute on my model. In my searching I have not seen a good method to overcome this problem.

My end goal would be to have a nice MVC application that heavily leverages JSON for communicating with the server and displaying data. While maintaining a consistant model across layers (or as best as I can come up with).

5

I see two distinct subjects in your question:

  • How to manage circular references when serializing to JSON?
  • How safe is it to use EF entities as model entities in you views?

Concerning circular references I'm sorry to say that there is no simple solution. First because JSON cannot be used to represent circular references, the following code:

var aParent = {Children : []}, aChild  = {Parent : aParent};
aParent.Children.push(aChild);
JSON.stringify(aParent);

Results in: TypeError: Converting circular structure to JSON

The only choice you have is to keep only the composite --> component part of the composition and discard the "back navigation" component --> composite, thus in you example:

class parent
{
    public List<child> Children{get;set;}
    public int Id{get;set;}
}
class child
{
    public int ParentId{get;set;}
    [ForeignKey("ParentId"), ScriptIgnore]
    public parent MyParent{get;set;}
    public string name{get;set;}
}

Nothing prevents you from recomposing this navigation property on your client side, here using jQuery:

$.each(parent.Children, function(i, child) {
  child.Parent = parent;  
})

But then you will need to discard it again before sending it back to the server, for JSON.stringify won't be able to serialize the circular reference:

$.each(parent.Children, function(i, child) {
  delete child.Parent;  
})

Now there is the issue of using EF entities as your view model entities.

First EF is likely to using Dynamic Proxies of your class to implement behaviors such as change detection or lazy loading, you have to disable those if you want to serialize the EF entities.

Moreover using EF entities in the UI can be at risk since all the default binder will mapping every field from the request to entities fields including those you did not want the user to set.

Thus, if you want you MVC app to be properly designed I would recommended to use a dedicated view model to prevent the "guts" of your internal business model from being exposed to the client, thus I would recommend you a specific view model.

  • Is there a fancy way with object oriented techniques I can get around both the circular reference and the EF issue. – DanScan Mar 27 '14 at 20:10
  • Is there a fancy way with object oriented techniques I can get around both the circular reference and the EF issue? Like BaseObject is inherited by entityObject and by viewObject. So entityObject would have the circular reference but viewObject would not have the circular reference. I have gotten around this by building viewObject from entityObject (viewObject.name = entityObject.name) but this seems to be a waste of time. How can I get around this issue? – DanScan Mar 27 '14 at 20:16
  • They you very much. Your explanation was very clear and easy to understand. – Nicholas V. Mar 16 '16 at 0:46
2

A simpler alternative to attempting to serialize the objects would be to disable the serialization of parent/child objects. Instead, you could make a separate call to fetch the associated parent/child objects as and when you need them. This may not be ideal for your application, but it's an option.

To do this, you could set up a DataContractSerializer and set the DataContractSerializer.PreserveObjectReferences property to 'false' in the constructor of your data model class. This specifies that the object references should not be preserved on serializing the HTTP responses.

Examples:

Json format:

var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = 
    Newtonsoft.Json.PreserveReferencesHandling.None;

XML format:

var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
var dcs = new DataContractSerializer(typeof(Employee), null, int.MaxValue, 
    false, /* preserveObjectReferences: */ false, null);
xml.SetSerializer<Employee>(dcs);

This means that if you fetch an item which has child objects referenced, the child objects won't be serialized.

See also the DataContractsSerializer class.

1

JSON serializer that deals with Circular References

Here is an example custom Jackson JSONSerializer that deals with circular references by serializing the first occurrence and storing a *reference to the first occurrence on all subsequent occurrences.

Dealing with Circular References when Serializing objects with Jackson

Relevant partial snippet from the above article:

private final Set<ObjectName> seen;

/**
 * Serialize an ObjectName with all its attributes or only its String representation if it is a circular reference.
 * @param on ObjectName to serialize
 * @param jgen JsonGenerator to build the output
 * @param provider SerializerProvider
 * @throws IOException
 * @throws JsonProcessingException
 */
@Override
public void serialize(@Nonnull final ObjectName on, @Nonnull final JsonGenerator jgen, @Nonnull final SerializerProvider provider) throws IOException, JsonProcessingException
{
    if (this.seen.contains(on))
    {
        jgen.writeString(on.toString());
    }
    else
    {
        this.seen.add(on);
        jgen.writeStartObject();
        final List<MBeanAttributeInfo> ais = this.getAttributeInfos(on);
        for (final MBeanAttributeInfo ai : ais)
        {
            final Object attribute = this.getAttribute(on, ai.getName());
            jgen.writeObjectField(ai.getName(), attribute);
        }
        jgen.writeEndObject();
    }
}
0

The one solution I have come up with is to create "View" Models. I create simple versions of the database models that do not include the reference to the parent class. These view models each have method to return the Database Version and a constructor that takes the database model as a parameter (viewmodel.name = databasemodel.name). This method seems forced although it works.

Sending out the bare minimum of data is the only correct answer. When you send out data from the database, it usually doesn't make sense to send out every single column with all the associations. The consumers shouldn't need to deal with database associations and structures, that is for databases. Not only will this save bandwidth but it's also much easier to maintain, read and consume. Query the data and then model it for what you actually need to send out eq. the bare minimum.

  • More processing time required when you're talking about big data, as now you have to transform everything twice. – mrdavenz Oct 23 '17 at 4:38
-2

.Include(x => x.TableName ) not returning relationships (from principal table to dependent table), or only returning one row of data, FIX HERE:

https://stackoverflow.com/questions/43127957/include-not-working-in-net-core-returns-one-parent

Also, in Startup.cs make sure you have this at the top:

using Microsoft.EntityFrameworkCore; 
using Newtonsoft.Json; 
using Project_Name_Here.Models;

protected by gnat Mar 31 '17 at 8:07

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

Not the answer you're looking for? Browse other questions tagged or ask your own question.