0

I'm struggling with net core returning truncated response. I have already defined no reference loop in my startup services, and also tried to set compatibility version for the version i'm currently using 2.1 as follows:

services.AddMvc()
        .SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
        .AddJsonOptions(x => x.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);

I also tried to serialize the array using JsonConvert and it did not throw any reference loop exception. Here's the action in the controller and the serializedArray text:

 public IActionResult GetProductItems(int productId)
 {
     try
     {
         var productItems = _productsMethods.GetProductItems(productId);

         // for testing the object for ref loops
         string serialized = Newtonsoft.Json.JsonConvert.SerializeObject(productItems);

         return Ok(productItems);
     }
     catch (ClientException ex)
     {
         return BadRequest(new { message = ex.Message });
     }
     catch (Exception ex)
     {
         return StatusCode(500, new { message = ex.Message });
     }
 }

// serialized string
//[{"ID":2,"ProductId":6,"ItemId":4,"Product":null,"Item":null,"Orders":[]},{"ID":3,"ProductId":":6,"ItemId":1,"Product":null,"Item":null,"Orders":[]},{"ID":5,"ProductId":":6,"ItemId":2,"Product":null,"Item":null,"Orders":[]}]

Here's the actual response

[{"id":2,"productId":6,"itemId":4,"product":null,"item":null,"orders":

Method:

public List<ProductItem> GetProductItems(int productId)
{
    IQueryable<DataSets.ProductItem> query = db.ProductItems
        .AsNoTracking()
        .Include(k => k.Orders)
        .Where(k => k.ProductId == productId);

    // result truncated (when array orders is empty)
    //return query.Select(_mapper.Map<ProductItem>).ToList();

    // without automapper, also truncated
    //return query.Select(k => new ProductItem()
    //{
    //    ID = k.ID,
    //    ItemId = k.ItemId,
    //    ProductId = k.ProductId,
    //    Orders = k.Orders.Select(a => new Order() { ID = a.ID })
    //    .ToList()
    //}).ToList();

    // WORKS, not getting truncated 
    // order not included 
    return query.Select(k => new ProductItem()
    {
        ID = k.ID,
        ItemId = k.ItemId,
        ProductId = k.ProductId,
    }).ToList();
}

Entities (renamed and removed props for simplification):

public class Product
{
    public int ID { get; set; }
  
    // some props

    public string UserId { get; set; }
    public User User { get; set; }

    public List<ProductItem> Items { get; set; }
}

public class ProductItem
{
    public int ID { get; set; }

    // some props

    public int ProductId { get; set; }
    public int ItemId { get; set; }

    public Product Product { get; set; }
    public Item Item { get; set; }

    public List<Order> Orders { get; set; }
}

public class Order
{
    public int ID { get; set; }
    
    // some props

    public int ItemId { get; set; }
    public ProductItemOrder Item { get; set; }
}

Since there is no reference loop in orders and also the reference loop is ignored. Why is this still truncating?

Ali Kleit
  • 3,069
  • 2
  • 23
  • 39
  • Please share your entities with their referances as well. – rcanpahali Oct 13 '20 at 13:33
  • @shadowman_93 added the entities and their references. – Ali Kleit Oct 13 '20 at 13:53
  • Obvious check, but I need it to ask it, are you sure that `productItems` contains a list of product items with `Product` and `Item` properties not null? Did you actually see them not null when inspecting in debugger? Please double check if you didn't just forget an include. – Prolog Oct 13 '20 at 18:40
  • @Prolog they are null, the string `serialized` is the same result I got from inspecting it. the only include made is `Orders`. and the arrays are emtpy. – Ali Kleit Oct 13 '20 at 18:48
  • Ok, from your question I know what is the actual behavior, but what is the behavior you are expecting? Which property `Item`, `Product` or `Orders` is getting truncated, but it shouldn't because it's not null/empty? – Prolog Oct 13 '20 at 18:55
  • @Prolog i have edited my question and added the actual method. when adding order (list) that issue occures. also i already included the actual response where on "orders" the response is truncated. – Ali Kleit Oct 13 '20 at 19:05

2 Answers2

0

I think the problem is public Product Product { get; set; } part here. You should define your Product entity virtually.

Here is an example below,

public class Blog
{
    public int BlogId { get; set; }
    public string Name { get; set; }
    public string Url { get; set; }
    public string Tags { get; set; }

    public virtual ICollection<Post> Posts { get; set; }
}

Here is source.

rcanpahali
  • 2,565
  • 2
  • 21
  • 37
  • Thanks but I tried that and unfortunatly it didn't work. I dont think it has to do with using `virtual` nor the props since there are no navigation loops in the response (tested in serialized string). – Ali Kleit Oct 13 '20 at 14:21
  • In EF `virtual` has a role, in EF Core (as long as you are not using lazy loading with proxies) no loger does, see [this question](https://stackoverflow.com/questions/41881169/navigation-property-should-be-virtual-not-required-in-ef-core). – Prolog Oct 13 '20 at 18:35
0

For someone who might have this issue in the future. While I think the API should throw that error instead of just truncating the response.

I had 2 properties with the same letters but different letter case IPAddress and IpAddress.

SerializeObject alone wasn't throwing an exception, then I did this (CamelCase Resolver) to point out the issue:

Newtonsoft.Json.JsonConvert.DefaultSettings = () => new Newtonsoft.Json.JsonSerializerSettings
{
    NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore,
    ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
};

string serialized = Newtonsoft.Json.JsonConvert.SerializeObject(productItems);

So it threw: A member with the name 'ipAddress' already exists on ...

Ali Kleit
  • 3,069
  • 2
  • 23
  • 39