locked
Recursive cicle in my api controller RRS feed

  • Question

  • User-1781360889 posted

    Hi, i am making an ecommerce app in asp net core 2.1. I got two (2) tables in my database: Business and Product. i Got a problem when i make my Api. I don t know if i got right the maner that i draw my database. In the business Table i put an ICollection Products, while in the Product Table i put a Business. My expectative is when i have a list of Businesses i would like to include the Products in these Businesses

    When i have a list of Products i need to include the Business too.

     Business TABLE
      public class Business
        {
     
            public int Id { get; set; }
     
           public string Name { get; set; }
           
            public string Adress { get; set; }
            public ICollection<Product> Products { get; set; }
     
            public ICollection<BusinessImage> BusinessImages { get; set; }
     
           public CategoryBusiness CategoryBusiness{ get; set; }
            public ICollection<Qualification> Qualifications { get; set; }
     
            [Required]
            public bool IsActive { get; set; }
     
            [NotMapped]
            public virtual IEnumerable<SelectListItem> CategoryBusinesses { get; set; }
     
            [NotMapped]
            public virtual IFormFile ImageFile { get; set; }
     
        }
    }

    Product TABLE

        public class Product
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public string Description { get; set; }
            public decimal Price { get; set; }
            public bool IsActive { get; set; }
           
            public bool IsStarred { get; set; }
     
            public User User { get; set; }
     
            public Category Category { get; set; }
     
           public Business Business { get; set; } 
         
          public ICollection<Qualification> Qualifications { get; set; }
     
            [DisplayName("Product Qualifications")]
            public int ProductQualifications => Qualifications == null ? 0 : Qualifications.Count;
     
            [DisplayFormat(DataFormatString = "{0:N2}")]
            public float Qualification => Qualifications == null || Qualifications.Count == 0 ? 0 : Qualifications.Average(q => q.Score);
        }

    API Business CONTROLLER

        [ApiController]
        [Route("api/[controller]")]
        public class BusinessesController : ControllerBase
        {
            private readonly DataContext _context;
     
            private readonly IMapper _mapper;
     
            public BusinessesController(DataContext context, IMapper mapper)
            {
                _context = context;
     
                _mapper = mapper;
            }
     
            [HttpGet]
               public async Task<IActionResult> GetBusinesses()
               {
               
                  List<Business> businesses = await _context.Businesses
                     .Include(p => p.CategoryBusiness)
                     .Include(p => p.User.City)
                     .Include(p => p.BusinessImages)
                     .Include(p => p.Qualifications)
                     .Include(p => p.Products)  // When i include product in Business and Business in Product i got a incomplete Json 
                   .ToListAsync(); 
     
                   return Ok(businesses);  
               
        } 
    }

     
     
     

    Api Product Controller

        [ApiController]
        [Route("api/[controller]")]
        public class ProductsController : ControllerBase
        {
            private readonly DataContext _context;
     
            private readonly IMapper _mapper;
     
            public ProductsController(DataContext context, IMapper mapper)
            {
                _context = context;
                _mapper = mapper;
            }
          
            [HttpGet]
            public async Task<IActionResult> GetProducts()
            {
                List<Product> products = await _context.Products
                     .Include(p => p.Category)
                     .Include(p => p.ProductImages)
                     .Include(p => p.Qualifications) 
                     .Include(p => p.Business) 
                   //   .ThenInclude(p => p.BusinessImages)  
                     .ToListAsync(); 
                return Ok(products);  */
     
                
            }
     
        }
    }

    JSON Api/Businesses (Incomplete because in product there is a field Business too)

    [{"id":1,"name":"Luna","adress":"re","height":3,
    "openingTime":"4","closingTime":"6",
    "imageId":"00000000-0000-0000-0000-000000000000",
    "products":[{"id":1,"name":"banana","description":"t","price":4.00,"isActive":true,
    "isStarred":false,"user":null,"category":null

    Json Api/Products (Incomplete too)

    How can i fix this?

    Sunday, May 23, 2021 11:55 PM

Answers

  • User1686398519 posted

    Hi KHP18, 

    The API will automatically serialize the data you return, but there is a self-reference cycle in the data you return, so you can manually serialize (set ReferenceLoopHandling) first, and then return jsonstring.

    The following is a demo modified according to your code, you can refer to it.

    Model

        public class Business
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public string Adress { get; set; }
            public ICollection<Product> Products { get; set; }
            [Required]
            public bool IsActive { get; set; }
            [NotMapped]
            public virtual IEnumerable<SelectListItem> CategoryBusinesses { get; set; }
            [NotMapped]
            public virtual IFormFile ImageFile { get; set; }
        }
    
    
        public class Product
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public string Description { get; set; }
            public decimal Price { get; set; }
            public bool IsActive { get; set; }
            public bool IsStarred { get; set; }        
            public Business Business { get; set; }
        }

    Controller

            [HttpGet]
            public async Task<IActionResult> GetProducts()
            {
                List<Product> products = await _context.Products                  
                    .Include(p => p.Business)
                   .ToListAsync();
                string jsonString = JsonConvert.SerializeObject(products,Formatting.None,
                            new JsonSerializerSettings()
                            {
                                ReferenceLoopHandling = ReferenceLoopHandling.Ignore
                            });
                return Ok(jsonString);
            }

    It’s just a friendly reminder, you can use {;} to insert the code to make it look clearer.

    Here is the result. 

    Best Regards,

    YihuiSun

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Monday, May 24, 2021 7:50 AM

All replies

  • User1686398519 posted

    Hi KHP18, 

    The API will automatically serialize the data you return, but there is a self-reference cycle in the data you return, so you can manually serialize (set ReferenceLoopHandling) first, and then return jsonstring.

    The following is a demo modified according to your code, you can refer to it.

    Model

        public class Business
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public string Adress { get; set; }
            public ICollection<Product> Products { get; set; }
            [Required]
            public bool IsActive { get; set; }
            [NotMapped]
            public virtual IEnumerable<SelectListItem> CategoryBusinesses { get; set; }
            [NotMapped]
            public virtual IFormFile ImageFile { get; set; }
        }
    
    
        public class Product
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public string Description { get; set; }
            public decimal Price { get; set; }
            public bool IsActive { get; set; }
            public bool IsStarred { get; set; }        
            public Business Business { get; set; }
        }

    Controller

            [HttpGet]
            public async Task<IActionResult> GetProducts()
            {
                List<Product> products = await _context.Products                  
                    .Include(p => p.Business)
                   .ToListAsync();
                string jsonString = JsonConvert.SerializeObject(products,Formatting.None,
                            new JsonSerializerSettings()
                            {
                                ReferenceLoopHandling = ReferenceLoopHandling.Ignore
                            });
                return Ok(jsonString);
            }

    It’s just a friendly reminder, you can use {;} to insert the code to make it look clearer.

    Here is the result. 

    Best Regards,

    YihuiSun

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Monday, May 24, 2021 7:50 AM
  • User-1781360889 posted

    wow thanks YihuiSun. Its worked.

    Monday, May 24, 2021 9:59 AM