21

I am using the Entity Framework code first for data access and I have a Company class which has a collection of Employees. The Employee class also has a Company property.

I would like to be able to serialize a Company and include the list of employees in the serialization.

Here is Company:

public class Company
{
public long Id { get; set; }
public string Name { get; set; }
public DateTime? Established { get; set; }

public virtual IList<Employee> Employees { get; set; }

public DateTime? DateCreated { get; set; }
public DateTime? DateUpdated { get; set; }
}

Here is Employee

public class Employee
{
public long Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }

public virtual Company Company { get; set; }

public DateTime? DateCreated { get; set; }
public DateTime? DateUpdated { get; set; }
}

I get a serialization Exception "Self referencing loop detected for type" when I try to serialize a Company object.

Thanks.

Emad
  • 4,110
  • 5
  • 30
  • 35
  • 1
    Possible duplicate of [JSON.NET Error Self referencing loop detected for type](https://stackoverflow.com/questions/7397207/json-net-error-self-referencing-loop-detected-for-type) – NH. Oct 31 '17 at 20:32
  • I know this question is (slightly) older, but that one has a better accepted answer. That is also where I am going to send all the other duplicates. – NH. Oct 31 '17 at 20:34

5 Answers5

17

I think they have fixed this in the latest version.

Check out the help docs under the section "Serializing and Deserializing JSON -> Serialization and Preserving Object References".

Set this setting when initializing the JSON.Net Serializer:

PreserveReferencesHandling = PreserveReferencesHandling.Objects;

So an example would be this:

var serializerSettings = new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects };

string json = JsonConvert.SerializeObject(people, Formatting.Indented, serializerSettings);

I verified that this works with my code first solution, and a circular reference in the navigation properties. If you look at the resulting JSON it should have "$id" and "$ref" properties everywhere.

John B
  • 20,062
  • 35
  • 120
  • 170
  • Wrote a blog post on the subject: http://www.johnnycode.com/blog/2012/04/10/serializing-circular-references-with-json-net-and-entity-framework/ – John B Apr 10 '12 at 18:36
  • Pointed me in the right direction. Note the JsonFormatter is now baked in to WebApi so all you need to do is set the PreserveReferencesHandling setting. – Kildareflare May 27 '15 at 07:34
  • Did u ever figure this out I need the relationship their when am posting the json because A student has many bookings in the gym – c-sharp-and-swiftui-devni Oct 03 '21 at 14:16
7

If you are getting this error using WebApi you can put the following in WebApiConfig.cs so json.net ignores circular refs

config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore; 

Microsoft : Loop Reference handling in Web API

Justin Moore
  • 827
  • 10
  • 12
6

In case this helps anyone, I thought I'd document how we resolved this same error for our purposes when using Entity Framework 4.3.1 and JSON.Net 4.5.3.

We are using the Database First DbContext approach. For our needs, we could resolve it using the [JsonIgnore] attribute. The trick is just that since changes to the automatically generated entity classes are overwritten when you refresh from the database, with Database First you can add the attributes using the "metadata buddy class" approach given in this StackOverflow post.

Below is a code excerpt. We had a "Query" object (class Query) that had relationships to "Company" and "User" objects. In a new class file, we declare the partial class with a [MetadataType] attribute, and then in the QueryMetadata class we specified, we annotate the members we want to ignore— namely the public virtual members that EF4.x adds to express the relationships (a.k.a. navigation properties).

The Query entity also has foreign key fields (named FK_User and FK_Company in our case). These fields do not need the [JsonIgnore] attribute— they can be serialized with their foreign key values.

[MetadataType(typeof(QueryMetadata))]
public partial class Query
{
}


public class QueryMetadata
{
    [JsonIgnore]
    public virtual Company company { get; set; }
    [JsonIgnore]
    public virtual User user { get; set; }
}

However, if we actually had to also serialize the related Company or User objects, we'd hit a brick wall! The approach suggested by John Bubriski here wouldn't work for us since we want to rely on Entity Framework change tracking.

Community
  • 1
  • 1
Martin_W
  • 1,582
  • 1
  • 19
  • 24
6

Updated Answer

You can either:

  • reconfigure json.net to ignore selfreference loops
  • use the [JsonIgnore] Attribute
  • use a custom converter that remove the navigation in the child
  • or you can use Data Transfer Objects
sra
  • 23,820
  • 7
  • 55
  • 89
  • I tried that and it caused a stack overflow i.e. Infinite loop... Also, how did you configure it to ignore self references. Maybe you did it some other way that works... – Emad Apr 24 '11 at 13:56
  • Then you have design problem. You need to remove one reference or use POCOs that have only one reference on employeeside. I am on my smartphone so I am not able to check it. I think the employee reference on client is not correct. – sra Apr 24 '11 at 14:11
  • Can you elaborate what you mean by "design problem"? The classes are super simple. Company has an IList property and Employee has a Company property. – Emad Apr 25 '11 at 17:13
  • Not the best but probably a solution could be to create data access objects without the navigation. I am doing the same in some of my projects. But that will only work if you don't require the navigation on the other side. – sra Apr 27 '11 at 11:20
  • I just started a new project and come along this. I am using Data Transfer Objects which now solves my Problem. But you can also use the [JsonIgnore]. I will update my post with this. – sra Apr 28 '11 at 09:51
1

If you're using WebAPI EntityFrameworkCore 2.0 this solution doesn't work, you need to set it on Startup.cs->ConfigureServices:

.AddJsonOptions(options => {
                    var settings = options.SerializerSettings;
                    settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
                });
cesar-moya
  • 581
  • 5
  • 8