JsonException: A possible object cycle was detected which is not supported. This can either be due to a cycle or if the object depth is larger than

35,927

Solution 1

this is happening because your data have a reference loop.

e.g

// this example creates a reference loop
var p = new Product()
     { 
        ProductCategory = new ProductCategory() 
           { products = new List<Product>() }
     };
    p.ProductCategory.products.Add(p); // <- this create the loop
    var x = JsonSerializer.Serialize(p); // A possible object cycle was detected ...

You can not handle the reference loop situation in the new System.Text.Json yet (netcore 3.1.1) unless you completely ignore a reference and its not a good idea always. (using [JsonIgnore] attribute)

but you have two options to fix this.

  1. you can use Newtonsoft.Json in your project instead of System.Text.Json (i linked an article for you)

  2. Download the System.Text.Json preview package version 5.0.0-alpha.1.20071.1 from dotnet5 gallery (through Visual Studio's NuGet client):

option 1 usage:

services.AddMvc()
     .AddNewtonsoftJson(
          options => {
           options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; 
      });
// if you not using .AddMvc use these methods instead 
//services.AddControllers().AddNewtonsoftJson(...);
//services.AddControllersWithViews().AddNewtonsoftJson(...);
//services.AddRazorPages().AddNewtonsoftJson(...);

option 2 usage:

// for manual serializer
var options = new JsonSerializerOptions
{
    ReferenceHandling = ReferenceHandling.Preserve
};

string json = JsonSerializer.Serialize(objectWithLoops, options);

// -----------------------------------------
// for asp.net core 3.1 (globaly)
 services.AddMvc()
  .AddJsonOptions(o => {
     o.JsonSerializerOptions
       .ReferenceHandling = ReferenceHandling.Preserve  
            });

these serializers have ReferenceLoopHandling feature.

  • Edit : ReferenceHandling changed to ReferenceHandler in DotNet 5

but if you decide to just ignore one reference use [JsonIgnore] on one of these properties. but it causes null result on your API response for that field even when you don't have a reference loop.

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string ProductText { get; set; }
    
    public int ProductCategoryId { get; set; }
    // [JsonIgnore] HERE or
    public virtual ProductCategory ProductCategory { get; set; }
}

public class ProductCategory
{
    public int Id { get; set; }
    // [JsonIgnore] or HERE
    public ICollection<Product> products {get;set;}
}

Solution 2

I have the same issue, my fix was to add async and await keyword since I am calling an async method on my business logic.

Here is my original code:

[HttpGet]
public IActionResult Get()
{
   //This is async method and I am not using await and async feature .NET which triggers the error
   var results = _repository.GetAllDataAsync(); 
   return Ok(results);
}

To this one:

HttpGet]
public async Task<IActionResult> Get()
{
   var results = await _repository.GetAllDataAsync();
   return Ok(results);
}

Solution 3

.NET 5 Web API

    public static void ConfigureServices(this IServiceCollection services, IConfiguration configuration)
    {
        services.AddControllers()
            .AddJsonOptions(o => o.JsonSerializerOptions
                .ReferenceHandler = ReferenceHandler.Preserve);
    }

Solution 4

Ensure you have [JsonIgnore] on the correct fields to avoid a circular reference.

In this case you will need

 public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string ProductText { get; set; }
    [JsonIgnore]
    public virtual ProductCategory ProductCategory { get; set; }
}

You probably don't need the ProductCategoryId field (depends if you are using EF and code first to define your DB)

Edit - In answer to noruk

There is often confusion in connected objects and navigation properties. You can get the data you want in JSON but also define the EF structures to get the correct DB structure (foreign keys, indexes, etc).

Take this simple example. A Product (for example a T-Shirt) has many sizes or SKUs (e.g. Small, Large, etc)

  public class Product
    {
     [Key]
     [MaxLength(50)]
     public string Style { get; set; }
     [MaxLength(255)]
     public string Description { get; set; }
     public List<Sku> Skus { get; set; }
    }
    
    public class Sku
    {
      [Key]
      [MaxLength(50)]
      public string Sku { get; set; }
      [MaxLength(50)]
      public string Barcode { get; set; }
      public string Size { get; set; }
      public decimal Price { get; set; }
      // One to Many for Product
      [JsonIgnore]
      public Product Product { get; set; }
    }

Here you can serialise a Product and the JSON data will include the SKUs. This is the normal way of doing things.

However if you serialise a SKU you will NOT get it's parent product. Including the navigation property will send you into the dreaded loop and throw the "object cycle was detected" error.

I know this is limiting in some use cases but I would suggest you follow this pattern and if you want the parent object available you fetch it separately based on the child.

var parent = dbContext.SKUs.Include(p => p.Product).First(s => s.Sku == "MY SKU").Product

Solution 5

For net core 3.1 you have to add in Startup.cs:

services.AddMvc.AddJsonOptions(o => {
o.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.Preserve;
o.JsonSerializerOptions.MaxDepth = 0;
})

and import at least this package using nuget.org include prerelease:

<PackageReference Include="System.Text.Json" Version="5.0.0-rc.1.20451.14" />
Share:
35,927
sunny
Author by

sunny

c# from 2003, .net Developer, xamarin, Angular,

Updated on February 08, 2022

Comments

  • sunny
    sunny over 2 years

    In my web api when i run project for get data from database got this error .net core 3.1

    JsonException: A possible object cycle was detected which is not supported. This can either be due to a cycle or if the object depth is larger than the maximum allowed depth of 32.

    These are my code my Model

     public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string ProductText { get; set; }
        public int ProductCategoryId { get; set; }
        [JsonIgnore]
        public virtual ProductCategory ProductCategory { get; set; }
    }
    

    my productCategory class is:

     public class ProductCategory
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string CatText { get; set; }
        public string ImagePath { get; set; }
        public int Priority { get; set; }
        public int Viewd { get; set; }
        public string Description { get; set; }
        public bool Active { get; set; }
        public DateTime CreateDate { get; set; }
        public DateTime ModifyDate { get; set; }
        public virtual ICollection<Product> Products { get; set; }
    }
    

    my repo is

    public async Task<IList<Product>> GetAllProductAsync()
        {
            return await  _context.Products.Include(p => p.ProductCategory).ToListAsync(); 
        }
    

    my interface

    public interface IProductRepository
    {
       ...
        Task<IList<Product>> GetAllProductAsync();
     ...
    }
    

    and this is my controller in api project

     [Route("api/[controller]")]
    [ApiController]
    public class ProductsController : ControllerBase
    {
        private readonly IProductRepository _productRepository;
    
        public ProductsController(IProductRepository productRepository)
        {
            _productRepository = productRepository;
        }
        [HttpGet]
        public ActionResult Get()
        {
            return Ok(_productRepository.GetAllProduct());
        }
    }
    

    When I run api project and put this url: https://localhost:44397/api/products I got that error , I cant resolve it