Entity framework self referencing loop detected [duplicate]
Asked Answered
A

13

117

I have a strange error. I'm experimenting with a .NET 4.5 Web API, Entity Framework and MS SQL Server. I've already created the database and set up the correct primary and foreign keys and relationships.

I've created a .edmx model and imported two tables: Employee and Department. A department can have many employees and this relationship exists. I created a new controller called EmployeeController using the scaffolding options to create an API controller with read/write actions using Entity Framework. In the wizard, selected Employee as the model and the correct entity for the data context.

The method that is created looks like this:

public IEnumerable<Employee> GetEmployees()
{
    var employees = db.Employees.Include(e => e.Department);
    return employees.AsEnumerable();
}

When I call my API via /api/Employee, I get this error:

The 'ObjectContent`1' type failed to serialize the response body for content type 'application/json; ...System.InvalidOperationException","StackTrace":null,"InnerException":{"Message":"An error has occurred.","ExceptionMessage":"Self referencing loop detected with type 'System.Data.Entity.DynamicProxies.Employee_5D80AD978BC68A1D8BD675852F94E8B550F4CB150ADB8649E8998B7F95422552'. Path '[0].Department.Employees'.","ExceptionType":"Newtonsoft.Json.JsonSerializationException","StackTrace":" ...

Why is it self referencing [0].Department.Employees? That doesn't make a whole lot of sense. I would expect this to happen if I had circular referencing in my database but this is a very simple example. What could be going wrong?

Academe answered 19/10, 2013 at 15:26 Comment(0)
F
191

Well the correct answer for the default Json formater based on Json.net is to set ReferenceLoopHandling to Ignore.

Just add this to the Application_Start in Global.asax:

HttpConfiguration config = GlobalConfiguration.Configuration;

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

This is the correct way. It will ignore the reference pointing back to the object.

Other responses focused in changing the list being returned by excluding data or by making a facade object and sometimes that is not an option.

Using the JsonIgnore attribute to restrict the references can be time consuming and if you want to serialize the tree starting from another point that will be a problem.

Fishbowl answered 13/5, 2015 at 0:6 Comment(7)
var config = GlobalConfiguration.Configuration.Formatters.JsonFormatter?Dragonnade
HttpConfiguration config = GlobalConfiguration.Configuration; //System.Web.HttpFishbowl
No Effect, exception still occursBullnecked
Doug Beard, Can you give me any information relative to your classes?Fishbowl
I get a self-referencing loop error even though I have the above code in Application_Start and have [JsonIgnore] on the relevant properties...I also have [IgnoreDataMember] on these properties, and have set ProxyCreationEnabled to false for entity framework...Highboy
Whenever i do this, i get the following exception: Attempted to read or write protected memory. This is often an indication that other memory is corruptGarlicky
Using DTO classes. #45330013Balakirev
A
56

This happens because you're trying to serialize the EF object collection directly. Since department has an association to employee and employee to department, the JSON serializer will loop infinetly reading d.Employee.Departments.Employee.Departments etc...

To fix this right before the serialization create an anonymous type with the props you want

example (psuedo)code:

departments.select(dep => new { 
    dep.Id, 
    Employee = new { 
        dep.Employee.Id, dep.Employee.Name 
    }
});
Adim answered 19/10, 2013 at 15:29 Comment(2)
In this case I would use a model class such as departments.select(dep => new { dep.Id, Employee = new ModelClass { Id = dep.Employee.Id, Name = dep.Employee.Name } });Beverlybevers
@AtulChaudhary it depends on what you want. If you simply want to consume the service via AJAX from an SPA, then this is sufficient, and I'd say better if you're doing MVVM, because it allows you to specify the ViewModel on the server. if you want to use this service as part of an N-Tier API, consumed by other .net services, then the other answer is more appropriate, because it keeps the database contract intact (as in, you'll still be sending a qualified EF entity).Adim
S
36

I had same problem and found that you can just apply the [JsonIgnore] attribute to the navigation property you don't want to be serialised. It will still serialise both the parent and child entities but just avoids the self referencing loop.

Surfperch answered 24/2, 2014 at 22:16 Comment(0)
B
22

I'm aware that question is quite old, but it's still popular and I can't see any solution for ASP.net Core.

I case of ASP.net Core, you need to add new JsonOutputFormatter in Startup.cs file:

    public void ConfigureServices(IServiceCollection services)
    {

        services.AddMvc(options =>
        {
            options.OutputFormatters.Clear();
            options.OutputFormatters.Add(new JsonOutputFormatter(new JsonSerializerSettings()
            {
                ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
            }, ArrayPool<char>.Shared));
        });

        //...
    }

After implementing it, JSON serializer will simply ignore loop references. What it means is: it will return null instead of infinitely loading objects referencing each other.

Without above solution using:

var employees = db.Employees.ToList();

Would load Employees and related to them Departments.

After setting ReferenceLoopHandling to Ignore, Departments will be set to null unless you include it in your query:

var employees = db.Employees.Include(e => e.Department);

Also, keep in mind that it will clear all OutputFormatters, if you don't want that you can try removing this line:

options.OutputFormatters.Clear();

But removing it causes again self referencing loop exception in my case for some reason.

Blatherskite answered 9/12, 2017 at 12:41 Comment(0)
D
9

The main problem is that serializing an entity model which has relation with other entity model(Foreign key relationship). This relation causes self referencing this will throw exception while serialization to json or xml. There are lots of options. Without serializing entity models by using custom models.Values or data from entity model data mapped to custom models(object mapping) using Automapper or Valueinjector then return request and it will serialize without any other issues. Or you can serialize entity model so first disable proxies in entity model

public class LabEntities : DbContext
{
   public LabEntities()
   {
      Configuration.ProxyCreationEnabled = false;
   }

To preserve object references in XML, you have two options. The simpler option is to add [DataContract(IsReference=true)] to your model class. The IsReference parameter enables oibject references. Remember that DataContract makes serialization opt-in, so you will also need to add DataMember attributes to the properties:

[DataContract(IsReference=true)]
public partial class Employee
{
   [DataMember]
   string dfsd{get;set;}
   [DataMember]
   string dfsd{get;set;}
   //exclude  the relation without giving datamember tag
   List<Department> Departments{get;set;}
}

In Json format in global.asax

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

in xml format

var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
var dcs = new DataContractSerializer(typeof(Employee), null, int.MaxValue, 
    false, /* preserveObjectReferences: */ true, null);
xml.SetSerializer<Employee>(dcs);
Doublethink answered 19/10, 2013 at 16:26 Comment(2)
Option 1 worked OK using the DataContract and DataMember attributes however at the end of the result I get "Employees":[{"$ref":"1"}]}}]. It has removed the symptom but the problem is still there. Surely everyone else would have exactly the same problem. Am I approaching this solution incorrectly?Academe
I set ProxyCreationEnabled = false and the jason thingy in the global.asax file. And it workes like a f**king charm. ThanksFloatable
G
8

The message error means that you have a self referencing loop.

The json you produce is like this example (with a list of one employee) :

[
employee1 : {
    name: "name",
    department : {
        name: "departmentName",
        employees : [
            employee1 : {
                name: "name",
                department : {
                    name: "departmentName",
                    employees : [
                        employee1 : {
                            name: "name",
                            department : {
                                and again and again....
                            }
                    ]
                }
            }
        ]
    }
}

]

You have to tell the db context that you don't want to get all linked entities when you request something. The option for DbContext is Configuration.LazyLoadingEnabled

The best way I found is to create a context for serialization :

public class SerializerContext : LabEntities 
{
    public SerializerContext()
    {
        this.Configuration.LazyLoadingEnabled = false;
    }
}
Genevivegenevra answered 1/9, 2014 at 8:4 Comment(0)
M
8

Add a line Configuration.ProxyCreationEnabled = false; in constructor of your context model partial class definition.

    public partial class YourDbContextModelName : DbContext
{
    public YourDbContextModelName()
        : base("name=YourDbContextConn_StringName")
    {
        Configuration.ProxyCreationEnabled = false;//this is line to be added
    }

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

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
    }
}
Modlin answered 23/6, 2017 at 7:32 Comment(1)
This work for me in 2017, the others solutions not workSouthland
F
6

I only had one model i wanted to use, so i ended up with the following code:

var JsonImageModel = Newtonsoft.Json.JsonConvert.SerializeObject(Images, new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore });
Fabrice answered 1/11, 2017 at 18:15 Comment(0)
D
6

If you are trying to change this setting in the Blazor (ASP.NET Core Hosted) template, you need to pass the following to the AddNewtonsoftJson call in Startup.cs in the Server project:

services.AddMvc().AddNewtonsoftJson(options => 
    options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore
);
Dyanna answered 9/5, 2019 at 12:56 Comment(0)
B
6

I just had the same issue on my .net core site. The accepted answer didn't work for me but i found that a combination of ReferenceLoopHandling.Ignore and PreserveReferencesHandling.Objects fixed it.

//serialize item
var serializedItem = JsonConvert.SerializeObject(data, Formatting.Indented, 
new JsonSerializerSettings
{
     PreserveReferencesHandling = PreserveReferencesHandling.Objects,
     ReferenceLoopHandling = ReferenceLoopHandling.Ignore
});
Banal answered 18/6, 2019 at 7:32 Comment(1)
When i am having a 1 to 1 relationship where the dependent entity does not have the foreign key in the model only the parent object , using ReferenceLoopHandling.Ignore will ignore also the parent object inside the child at serialization.Noddle
P
0

I might also look into adding explicit samples for each controller/action, as well covered here:

http://blogs.msdn.com/b/yaohuang1/archive/2012/10/13/asp-net-web-api-help-page-part-2-providing-custom-samples-on-the-help-page.aspx

i.e. config.SetActualResponseType(typeof(SomeType), "Values", "Get");

Propolis answered 23/7, 2014 at 21:27 Comment(0)
H
0

Self-referencing as example

public class Employee {
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Email { get; set; }
    public int ManagerId { get; set; }
    public virtual Employee Manager { get; set; }

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

    public Employee() {
        Employees = new HashSet<Employee>();
    }
}
HasMany(e => e.Employees)
    .WithRequired(e => e.Manager)
    .HasForeignKey(e => e.ManagerId)
    .WillCascadeOnDelete(false);
Heymann answered 9/7, 2018 at 7:54 Comment(0)
M
0

I know this is an old question but here is the solution I found to a very similar coding issue in my own code:

var response = ApiDB.Persons.Include(y => y.JobTitle).Include(b => b.Discipline).Include(b => b.Team).Include(b => b.Site).OrderBy(d => d.DisplayName).ToArray();
        foreach (var person in response)
        {
            person.JobTitle = new JobTitle()
            {
                JobTitle_ID = person.JobTitle.JobTitle_ID,
                JobTitleName = person.JobTitle.JobTitleName,
                PatientInteraction = person.JobTitle.PatientInteraction,
                Active = person.JobTitle.Active,
                IsClinical = person.JobTitle.IsClinical
            };
        }

Since the person object contains everything from the person table and the job title object contains a list of persons with that job title, the database kept self referencing. I thought disabling proxy creation and lazy loading would fix this but unfortunately it didn't.

For the that aren't able to do that, try the solution above. Explicitly creating a new object for each object that self references, but leave out the list of objects or object that goes back to the previous entity will fix it since disabling lazy loading does not appear to work for me.

Misrepresent answered 9/10, 2019 at 14:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.