I'm trying to get SubCategory after Include(p => p.SubCategory) and get the error:
"Newtonsoft.Json.JsonSerializationException: 'Self referencing loop detected with type 'E_Store2021.Models.Product'. Path '[0].Product.SubCategory.Products' "
I need to get SubCategoryName. What should I do in this case? Without Include() everything works fine. The same will happen with the company.
Markup:
<tr>
<td>
<figure class="itemside align-items-center">
<div class="aside"><img src="@("~/images/content/" + item.Product.ImagePath)" asp-append-version="true" class="img-sm"/></div>
<figcaption class="info">
<a href="#" class="title text-dark" data-abc="true">@item.Product.ProductName</a>
<p class="text-muted small">Category: <br> Brand: @item?.Product?.Company.CompanyName</p>
<p class="text-muted small">SubCategory: @item.Product?.SubCategory?.SubCategoryName</p>
</figcaption>
</figure>
</td>
<td>
<select class="form-control">
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
</select>
</td>
<td>
<div class="price-wrap"> <var class="price">@item.Product.UnitPrice</var> <small class="text-muted"> $9.20 each </small> </div>
</td>
<td class="text-right d-none d-md-block"> <a data-original-title="Save to Wishlist" title="" href="" class="btn btn-light" data-toggle="tooltip" data-abc="true"> <i class="fa fa-heart"></i></a> <a href="" class="btn btn-light" data-abc="true"> Remove</a> </td>
</tr>
Code:
namespace E_Store2021.Controllers
{
public class CartController : Controller
{
private ApplicationDbContext _context;
public CartController(ApplicationDbContext context)
{
_context = context;
}
[AllowAnonymous]
public IActionResult Index()
{
var cart = SessionHelper.GetObjectFromJson<List<ShoppingCartItem>>(HttpContext.Session, "cart");
ShoppingCartModel.ShoppingCartItems = cart;
ShoppingCartModel.Total = cart?.Sum(item => item.Product.UnitPrice * item.Quantity);
return View();
}
[AllowAnonymous]
public IActionResult Buy(int id)
{
if (SessionHelper.GetObjectFromJson<List<ShoppingCartItem>>(HttpContext.Session, "cart") == null)
{
List<ShoppingCartItem> cart = new List<ShoppingCartItem>();
cart.Add(new ShoppingCartItem { Product = _context.Products.Include(p=>p.SubCategory).FirstOrDefault(p => p.ProductID == id), Quantity = 1 });
SessionHelper.SetObjectAsJson(HttpContext.Session, "cart", cart);
}
else
{
List<ShoppingCartItem> cart = SessionHelper.GetObjectFromJson<List<ShoppingCartItem>>(HttpContext.Session, "cart");
int index = IsExist(id);
if (index != -1)
cart[index].Quantity++;
else
cart.Add(new ShoppingCartItem { Product = _context.Products.Include(p => p.SubCategory).FirstOrDefault(p => p.ProductID == id), Quantity = 1 });
SessionHelper.SetObjectAsJson(HttpContext.Session, "cart", cart);
}
return RedirectToAction("Index");
}
[AllowAnonymous]
public IActionResult Remove(int id)
{
List<ShoppingCartItem> cart = SessionHelper.GetObjectFromJson<List<ShoppingCartItem>>(HttpContext.Session, "cart");
int index = IsExist(id);
cart.RemoveAt(index);
SessionHelper.SetObjectAsJson(HttpContext.Session, "cart", cart);
return RedirectToAction("Index");
}
[AllowAnonymous]
private int IsExist(int id)
{
List<ShoppingCartItem> cart = SessionHelper.GetObjectFromJson<List<ShoppingCartItem>>(HttpContext.Session, "cart");
for (int i = 0; i < cart.Count; i++)
{
if (cart[i].Product.ProductID.Equals(id))
return i;
}
return -1;
}
}
}
SessionHelper. An error occurs here when trying to serialize an object.
public static class SessionHelper
{
public static void SetObjectAsJson(this ISession session, string key, object value)
{
session.SetString(key, JsonConvert.SerializeObject(value));
}
public static T GetObjectFromJson<T>(this ISession session, string key)
{
var value = session.GetString(key);
return value == null ? default : JsonConvert.DeserializeObject<T>(value);
}
}
Not sure if you've solved this already, since it was asked yesterday, or why there were no reactions.
This is a commonly encountered issue when there are circular references in the object structure. The best practice is to avoid these in the first place in your DTOs / viewmodels.
But if it's fine for you to exclude these circular references from the serialization, you're lucky, because you're using Newtonsoft JSON, which supports configuring this behavior via the JsonSerializerSettings.ReferenceLoopHandling
option.
I think this should be enough here, since it seems the self-referencing occurs between Products.SubCategory
and SubCategory.Products
, and what you want is SubCategory.Name
, which should still be populated.
If it's not feasible to use this setting, you can just define a ProductDto or ProductViewModel, which could perhaps even flatten the hierarchy, and contain - besides the other properties - a SubCategoryName
property instead of including the whole SubCategory
entity (or however works for you).
There are three ways to use this aforementioned option.
services.AddMvc()
.AddNewtonsoftJson(options => {
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
});
Keep in mind that if you were using ASP.NET Core 3+, and there were no existing AddNewtonsoftJson()
call in the configuration beforehand, doing this will replace Microsoft's new System.Text.Json
serializer with Newtonsoft's serializer (which is slower).
JsonConvert.DefaultSettings = () => new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
};
// Then all subsequent manual serialization will be done with this setting.
string json = JsonConvert.SerializeObject (object);
Note that configuring it this way won't change the behavior of ASP.NET Core controller actions, because that serialization appears to use a separate settings instance.
JsonSerializerSettings
instance each time explicitly when you're manually serializing:JsonSerializerSettings settings = new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
};
string json = JsonConvert.SerializeObject (object, settings);
So, in your case, solution 2) or 3) is what you need. And thanks for the edit; in my original answer I didn't take into consideration that you're manually calling SerializeObject()
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With