簡體   English   中英

JsonException: 檢測到不支持的可能的對象循環。 這可能是由於循環或對象深度大於

[英]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

在我的 web api 中,當我運行項目以從數據庫獲取數據時出現此錯誤 .net core 3.1

JsonException: 檢測到不支持的可能的對象循環。 這可能是由於循環或對象深度大於最大允許深度 32。

這些是我的代碼我的模型

 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; }
}

我的 productCategory 類是:

 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; }
}

我的回購是

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

我的界面

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

這是我在 api 項目中的控制器

 [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());
    }
}

當我運行 api 項目並輸入這個 url: https://localhost:44397/api/products我得到那個錯誤,我無法解決它

發生這種情況是因為您的數據有一個參考循環。

例如

// 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 ...

您還不能在新的System.Text.Json (netcore 3.1.1)中處理引用循環情況,除非您完全忽略引用並且它始終不是一個好主意。 (使用[JsonIgnore]屬性)

但是你有兩個選擇來解決這個問題。

  1. 你可以在你的項目中使用Newtonsoft.Json而不是System.Text.Json (我為你鏈接了一篇文章)

  2. 從 dotnet5 庫(通過 Visual Studio 的 NuGet 客戶端)下載System.Text.Json預覽包版本5.0.0-alpha.1.20071.1

選項 1 用法:

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(...);

選項 2 用法:

// 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  
            });

這些序列化程序具有ReferenceLoopHandling功能。

  • 編輯在 DotNet 5 中, ReferenceHandling更改為ReferenceHandler

但是如果您決定忽略一個引用,請在這些屬性之一上使用[JsonIgnore] 但即使您沒有引用循環,它也會導致該字段的 API 響應為空結果。

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;}
}

我有同樣的問題,我的解決方法是添加 async 和 await 關鍵字,因為我在我的業務邏輯上調用 async 方法。

這是我的原始代碼:

[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);
}

對此:

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

確保在正確的字段上有 [JsonIgnore] 以避免循環引用。

在這種情況下,您將需要

 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; }
}

您可能不需要 ProductCategoryId 字段(取決於您是否首先使用 EF 和代碼來定義您的數據庫)

編輯 - 回答noruk

在連接的對象和導航屬性中經常會出現混淆。 您可以在 JSON 中獲取所需的數據,還可以定義 EF 結構以獲取正確的數據庫結構(外鍵、索引等)。

舉這個簡單的例子。 產品(例如 T 恤)有多種尺寸或 SKU(例如小號、大號等)

  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; }
    }

您可以在此處序列化產品,JSON 數據將包括 SKU。 這是正常的做事方式。

但是,如果您序列化一個 SKU,您將不會獲得它的父產品。 包含導航屬性將使您進入可怕的循環並拋出“檢測到對象循環”錯誤。

我知道這在某些用例中是有限制的,但我建議您遵循這種模式,如果您希望父對象可用,您可以根據子對象單獨獲取它。

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

.NET 5 Web API

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

對於 net core 3.1,您必須在 Startup.cs 中添加:

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

並使用 nuget.org 至少導入這個包,包括預發布:

<PackageReference Include="System.Text.Json" Version="5.0.0-rc.1.20451.14" />

最后用System.Text.Json修復了我的而不是NewtonSoft.Json使用

var options = new JsonSerializerOptions()
        {
            MaxDepth = 0,
            IgnoreNullValues = true,
            IgnoreReadOnlyProperties = true
        };

使用選項進行序列化

objstr = JsonSerializer.Serialize(obj,options);

以下代碼在 dotnet 5.0 中對我有用:

  services.AddControllersWithViews()
                        .AddJsonOptions(o => o.JsonSerializerOptions
                        .ReferenceHandler = ReferenceHandler.Preserve);

我的項目構建時出現了類似的錯誤。

這是之前的代碼

public class PrimaryClass {
  public int PrimaryClassId
  public ICollection<DependentClass> DependentClasses { get; set; }
}

public class DependentClass {
  public int DependentClassId { get; set; }
  public int PrimaryClassId { get; set; }
  public PrimaryClass primaryClass { get; set; }
}

我從 DependentClass 模型中拿走了 PrimaryClass 對象。

代碼之后

public class PrimaryClass {
  public int PrimaryClassId
  public ICollection<DependentClass> DependentClasses { get; set; }
}

public class DependentClass {
  public int DependentClassId { get; set; }
  public int PrimaryClassId { get; set; }
}

我還必須調整OnModelCreating方法

modelBuilder.Entity<PrimaryClass>().HasMany(p => p.DependentClasses).WithOne(d => d.primaryClass).HasForeignKey(d => d.PrimaryClassId);

modelBuilder.Entity<PrimaryClass>().HasMany(p => p.DependentClasses);

正在運行的 DbSet 查詢是

public async Task<List<DependentClass>> GetPrimaryClassDependentClasses(PrimaryClass p)
{
  return await _dbContext.DependentClass.Where(dep => dep.PrimaryClassId == p.PrimaryClassId).ToListAsync();
}

錯誤可能與這 3 段代碼中的任何一段有關,但是從依賴類中刪除主要對象引用並調整OnModelCreating解決了錯誤,我只是不確定為什么會導致循環。

就我而言,問題在於創建實體關系時。 我像這樣在依賴實體中使用外鍵鏈接主實體

[ForeignKey("category_id")]
public Device_Category Device_Category { get; set; }

我還提到了主實體中的 dippendend 實體。

 public List<Device> devices { get; set; }

這創造了一個循環。

依賴實體

  public class Device
    {
        [Key]
        public int id { get; set; }
        public int asset_number { get; set; }
        public string brand { get; set; }
        public string model_name { get; set; }
        public string model_no { get; set; }
        public string serial_no { get; set; }
        public string os { get; set; }
        public string os_version { get; set; }
        public string note { get; set; }
        public bool shared { get; set; }
        public int week_limit { get; set; }
        public bool auto_acceptance { get; set; }
        public bool booking_availability { get; set; }
        public bool hide_device { get; set; }
        public bool last_booked_id { get; set; }


        //getting the relationships category 1 to many 
        public int category_id { get; set; }

        [ForeignKey("category_id")]
        public Device_Category Device_Category { get; set; }

        public List<Booking> Bookings { get; set; } 
       
    }

主要實體

    public class Device_Category
    {
        public int id { get; set; }
        public string name { get; set; }

        public List<Device> devices { get; set; }
    }
}

所以我評論了

public List<Device> devices { get; set; }

內部主要實體(Device_Category)和問題解決

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM