5

I really am done with this, but at least I want to know what's going on. Here we go:

My project is an ASP.NET Core Web Application with Code First Entity Framework Core and an Angular frontend.

I want to control when to load referenced objects. They can be useful, but they can also create circular references with internal errors on the frontend. (JSON would be infinitely long.)

Models:

class Book {
   public virtual ICollection<Page> Pages { get; set; }
   ...simple properties
}

class Page {
   public virtual Book Book { get; set; }
   ...simple properties
}

In this example, every book from books will have an empty/null Pages list.

using (var context = new MoneyStatsContext())
{
   var books = context.Books.Where(rule => rule.State == 1).ToList();
}

In this example, the Pages lists are not null, and every Page will have it's Book property set. Thus creating a circular reference.

using (var context = new MoneyStatsContext())
{
   var books = context.Books.Where(rule => rule.State == 1).Include(x => x.Pages).ToList();
}

How do I avoid the circular reference? Do I really have no other (simpler) choice than creating a new model and manually specifying each property?

.Select(new Book() {
   ...setting each property by hand
}

Not-working-solutions I've found:

  • Tried setting this false and true. Doesn't seem to change anything.
public MyContext()
{
   this.ChangeTracker.LazyLoadingEnabled = false;
}

  • Tried specifying this in Startup.cs, but options doesn't have a SerializerSettings property.
services.AddMvc().AddJsonOptions(options =>
{
   options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
});

Any help would be appreciated. Thanks in advance.

user2563731
  • 71
  • 1
  • 7

1 Answers1

15

Assuming you are using the latest and greatest ASP .NET Core 3.1 with System.Text.Json, to handle reference loops you will need to switch "back" to Newtonsoft.Json (though it worth mentioning that System.Text.Json should be faster. Also support for reference loops handling is coming, as @Eric J. wrote in comments):

services.AddControllers()
    .AddNewtonsoftJson(x => x.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore)

As for EF creating reference loops - it is called relationship fixup and you can't do a lot about it (see this answer). AsNoTracking can help a little bit (but not in case of Include).

My personal approach is to return DTO's from endpoints and not entities directly.

UPD

In .NET 5.0 ReferenceHandler is introduced, so next should do the trick:

services.AddControllersWithViews()
    .AddJsonOptions(options =>
        options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.Preserve)
Guru Stron
  • 102,774
  • 10
  • 95
  • 132
  • 1
    .NET Framework 5.0 will support circular references https://github.com/dotnet/runtime/issues/30820 – Eric J. Jul 19 '20 at 21:35
  • @EricJ. yes, but it is not released yet =( – Guru Stron Jul 19 '20 at 21:36
  • 1
    This didn't exactly work as described, but I could be at fault not providing enough information. `AddMvc()` returns an `IMvcBuilder` which doesn't have an `AddNewtonsoftJson` method. I had to add the `Microsoft.AspNetCore.Mvc.NewtonsoftJson` nuget package and use `AddControllers` instead of `AddMvc`. Source: https://stackoverflow.com/a/55666898/2563731 – user2563731 Jul 20 '20 at 17:49