Reputation: 101
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:
public MyContext()
{
this.ChangeTracker.LazyLoadingEnabled = false;
}
services.AddMvc().AddJsonOptions(options =>
{
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
});
Any help would be appreciated. Thanks in advance.
Upvotes: 8
Views: 16460
Reputation: 142113
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 so you can eliminate the cycles completely and fully control the returned model.
UPD
In .NET 5.0 ReferenceHandler
is introduced, so next should do the trick:
services.AddControllersWithViews()
.AddJsonOptions(options =>
options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.Preserve)
Upvotes: 19