I have REST API and Blazor application. I have following Model logic:
public class Offer
{
[Required]
public int OfferId { get; set; }
public string OfferName { get; set; }
public ICollection<Item> Items { get; set; }
}
public class Item
{
public int ItemId { get; set; }
public string PartNo { get; set; }
....
public int OfferId { get; set; }
public Offer Offer { get; set; }
public Nullable<int> ParentId { get; set; }
public Item Parent { get; set; }
public IEnumerable<Item> Children { get; set; }
}
Offer can have more items, every item can have children items. Let's assume that there can be only one level of children.
I wanted to ask rest API for list of items with their children. So I created controller like this:
[HttpGet("offer/{offerId}/{parentId}")]
public IActionResult GetItemsForOffer(int offerId, Nullable<int> parentId, [FromQuery] ItemParameters parameters)
{
if (parentId == 0) parentId = null;
var items = _repository.Item.GetItemsByOffer(offerId, parentId, parameters);
Response.Headers.Add("X-Pagination", JsonConvert.SerializeObject(items.MetaData));
return Ok(items);
}
And Repisotory looks like this:
public PagedList<Item> GetItemsByOffer(int offerId, Nullable<int> parentId, ItemParameters parameters)
{
var items = FindByCondition(a => a.OfferId.Equals(offerId) && a.ParentId == parentId )
.Include(item => item.Children)
.Sort(parameters.OrderBy);
return PagedList<Item>.ToPagedList(items,
parameters.PageNumber,
parameters.PageSize);
}
The code was working before I added .Include(item => item.Children)
PagedList (but this is not important for this case I think):
public class PagedList<T> : List<T>
{
public MetaData MetaData { get; set; }
public PagedList(List<T> items, int count, int pageNumber, int pageSize)
{
MetaData = new MetaData
{
TotalCount = count,
PageSize = pageSize,
CurrentPage = pageNumber,
TotalPages = (int)Math.Ceiling(count / (double)pageSize)
};
AddRange(items);
}
public static PagedList<T> ToPagedList(IQueryable<T> source, int pageNumber, int pageSize)
{
var count = source.Count();
var items = source.Skip((pageNumber - 1) * pageSize).Take(pageSize).ToList();
return new PagedList<T>(items, count, pageNumber, pageSize);
}
}
My idea of response is this:
[
{
"itemId": 1,
"partNo": "ABC",
...
"parentId": null,
"parent": null,
"children": [
{
"itemId": 2,
"partNo": "DEF",
"children": []
}
]
},
{
"itemId": 3,
"partNo": "GHI",
...
"parentId": null,
"parent": null,
"children": null
}
]
But I get this error instead: System.Text.Json.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.
I googled it and found similiar topic: .Net Core 3.0 possible object cycle was detected which is not supported
Best answer (I think) is "This happens because of the 2 way relationship between your data model when it comes to be JSON serialized.
You should not return your data model diectly. Map it to a new response model then return it."
So it seems my idea of using REST API is wrong. I thought that REST API should handle all joining and including. But maybe I should do it in a Blazor http repository? Or where is a proper place to do joining?
And while I am writing this, I realized that I actually dont need Children, I just need information that Children exists. But I dont know how to write it.