How to handle a Circular Reference Problem in ASP Dot Net MVC Core

How to  handle a Circular Reference Problem in ASP.NET MVC Core?


The code below contains a problem that causes so much confusion that it is worth exploring in detail. To see the problem, restart the ASP.NET Core MVC application and use a browser to request the URL

http://localhost:5000/api/products/1. The application will report the following exception:


Newtonsoft.Json.JsonSerializationException: Self referencing loop detected with

type 'SportsStore.Models.Product'. Path 'supplier.products'.


Ans : The Newtonsoft.Json namespace contains the excellent Json.NET package that Microsoft uses for JSON serialization in ASP.NET Core. Json.NET keeps track of the objects it serializes to avoid circular references that would lead to the same data being endlessly serialized. For example, in a situation where object A has a reference object B and object B has a reference to object A, there is a risk that the serializer will get stuck in a loop endlessly following the references between the objects. To avoid this situation, the serializer throws an exception when it follows a reference to an object that it has already serialized.

Looking at the code below, you might struggle to see why using the Include method has created a circular reference. The problem is caused by a well-intentioned Entity Framework Core feature that attempts to minimize the amount of data read from the database but that causes problems in ASP.NET Core MVC applications. To see what is happening, change the configuration of the JSON serializer in the Startup class so that it doesn’t report an exception when it detects a circular reference.

Configuring the JSON Serializer in the Startup.cs File in the SportsStore Folder

using Microsoft.AspNetCore.Builder;

using Microsoft.AspNetCore.Hosting;

using Microsoft.Extensions.Configuration;

using Microsoft.Extensions.DependencyInjection;

using Microsoft.Extensions.Logging;

using Microsoft.AspNetCore.SpaServices.Webpack;

using SportsStore.Models;

using Microsoft.EntityFrameworkCore;

using Newtonsoft.Json;


namespace SportsStore {

    public class Startup {

        public Startup(IHostingEnvironment env) {

            var builder = new ConfigurationBuilder()


              .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)

              .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)


            Configuration = builder.Build();



        public IConfigurationRoot Configuration { get; }


        public void ConfigureServices(IServiceCollection services) {

            services.AddDbContext<DataContext>(options =>




            services.AddMvc().AddJsonOptions(opts => {


                    = ReferenceLoopHandling.Serialize;

                opts.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;




        public void Configure(IApplicationBuilder app,

                IHostingEnvironment env, ILoggerFactory loggerFactory) {





            app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions {

                HotModuleReplacement = true





            app.UseMvc(routes => {


                    name: "default",

                    template: "{controller=Home}/{action=Index}/{id?}");









This doesn’t fix the problem, but it does reveal what is causing it, albeit by allowing a circular reference to create a terminal stack overflow. Restart the ASP.NET Core MVC application and use a browser to request the http://localhost:5000/api/products/1 URL. Instead of the exception shown earlier, the application

will terminate with this error:


Process is terminated due to StackOverflowException


This is the situation that the JSON serializer tries to avoid. You can see what caused the problem by examining the JSON content that was displayed by the browser before the application stopped, which is easier to understand if you format the data like this:


{ "productId":1, "name":"Kayak", "category":"Watersports",

"description":"A boat for one person", "price":275.00,


"supplierId":1, "name":"Splash Dudes", "city":"San Jose",



{ "productId":1, "name":"Kayak", "category":"Watersports",

"description":"A boat for one person", "price":275.00,


"supplierId":1,"name":"Splash Dudes", "city":"San Jose",



{ "productId":1, "name":"Kayak", "category":"Watersports",


When Entity Framework Core creates objects, it tries to populate navigation properties with objects that have already been created by the same database context. This can be a useful feature in some kinds of application, such as desktop apps, where a database context object has a long life and is used to make many requests over time. It isn’t useful for ASP.NET Core MVC applications where a new context object is created for each HTTP request. In the example application, the only objects that Entity Framework Core creates are the ones for the current query, which starts with a Product object and includes the related Supplier and Rating objects.

When Entity Framework Core creates the Supplier object, it looks at the objects it has already created to see whether any of them can be used to populate the Products navigation property. There is one such

object, which is the Product object that has already been created, so Entity Framework Core adds this to the collection assigned to the Supplier.Products property. This creates the circular reference, which isn’t a problem when the references are between objects in the .NET Core runtime but cause problems when they are serialized to JSON, which doesn’t have any way to represent references between objects.

The application terminates with a StackOverflowException because the JSON serializer follows these the references and writes out each object it encounters. The Product object has a reference to the Supplier object through its Supplier property, which has a reference to the Product object through its Products property, which has a reference to the Supplier object, and so on.


Breaking the Circular References

There is no way to stop Entity Framework Core from using existing objects for navigation properties.Preventing the problem means breaking the references between objects after they have been created by Entity Framework Core and before they are processed by the JSON serializer.

Breaking References in the ProductValuesController.cs File in the Controllers Folder

using Microsoft.AspNetCore.Mvc;

using SportsStore.Models;

using Microsoft.EntityFrameworkCore;

using System.Linq;

using System.Collections.Generic;


namespace SportsStore.Controllers {



    public class ProductValuesController : Controller {

        private DataContext context;


        public ProductValuesController(DataContext ctx) {

            context = ctx;




        public Product GetProduct(long id) {

            Product result = context.Products

                          .Include(p => p.Supplier).ThenInclude(s => s.Products)

                          .Include(p => p.Ratings)

                          .First(p => p.ProductId == id);


            if (result != null) {

                if (result.Supplier != null) {

                    result.Supplier.Products = result.Supplier.Products.Select(p =>

                        new Product {

                            ProductId = p.ProductId,

                            Name = p.Name,

                            Category = p.Category,

                            Description = p.Description,

                            Price = p.Price,




                if (result.Ratings != null) {

                    foreach (Rating r in result.Ratings) {

                        r.Product = null;




            return result;




        public IEnumerable<Product> GetProducts(string category, string search, 

                bool related = false) {

            IQueryable<Product> query = context.Products;


            if (!string.IsNullOrWhiteSpace(category)) {

                string catLower = category.ToLower();

                query = query.Where(p => p.Category.ToLower().Contains(catLower));


            if (!string.IsNullOrWhiteSpace(search)) {

                string searchLower = search.ToLower();

                query = query.Where(p => p.Name.ToLower().Contains(searchLower)

                    || p.Description.ToLower().Contains(searchLower));



            if (related) {

                query = query.Include(p => p.Supplier).Include(p => p.Ratings);

                List<Product> data = query.ToList();

                data.ForEach(p => {

                    if (p.Supplier != null) {

                        p.Supplier.Products = null;


                    if (p.Ratings != null) {

                        p.Ratings.ForEach(r => r.Product = null);



                return data;

            } else {

                return query;







To break the relationships, the listing sets the Supplier.Products property to null. There is also a circular reference between the Product and Rating objects, so the listing enumerates the Rating objects returned by the Product object’s Ratings property to set their Product property to null. To see the result, restart the ASP.NET Core MVC application and use a browser to request the http://localhost:5000/api/products/1 URL, which will produce the following data:



"description":"A boat for one person","price":275.00,


"supplierId":1,"name":"Splash Dudes","city":"San Jose",







There are two other ways to prevent the JSON serializer from throwing exceptions about circular references, both of which will show up if you search online for the error text, but neither of which really solves the problem.

The most common advice is to change the configuration of the serializer so that it simply ignores any object that it has already serialized, using a configuration statement like this in the Startup class:


services.AddMvc().AddJsonOptions(opts =>

opts.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore);


The Ignore setting prevents exceptions, but it doesn’t stop extraneous data from being returned by the web service, which can be a problem when you are handling requests for multiple objects. Each object retrieved from the database gives Entity Framework Core more scope to populate navigation properties, adding to the data sent to the client.

The other approach is to add the JsonIgnore attribute to navigation properties in the model classes to stop the serializer following them when creating JSON data. This solves the problem in the short term but prevents you from being able to create useful web services that deal with the model classes directly,rather than as related data.

(Visited 18 times, 1 visits today)

Leave a Reply

Your email address will not be published. Required fields are marked *