0

I have two model classes, Address and Product, they are in a many-to-many relationship on the Database.

When I try to load Addresses using Include() to also get the Products, I get a circularReferenceException when I try to convert it to a JSON. That makes perfect sense, the JSON would become infinitly long.

I know, that I can resolve this problem by putting [ScriptIgnore] on top of one referencing lists.

But that causes a new problem: I need to resolve the relationship in both directions and put them into a JSON, depending on the situation. By that I mean that, depending on the situation, I need Products and the referenced Addresses at another point I need Addresses an their Product references;

Here are my classes(shortened):

public class Product
{

    ...

    [ScriptIgnore]
    public List<Address> Addresses { get; set; }
}

Address class:

   public class Address
{
    ....

    public List<Product> Products { get; set; }

    ...
}

Getting the Data:

        public JsonResult GetAllOrders()
    {
        EFDbContext context = new EFDbContext();
        return Json(context.Addresses.Include(a => a.Products).ToList(), JsonRequestBehavior.AllowGet);
    }

Is there a Way to tell the serializer which references to ignore and which to honor? In the above situation I would like the Address->Product reference to be honored but the Product->Address in their children to be ignored.

The only solution that comes to my mind is to loop over each address and their Product and remove the references. But I hope there is a more elegant way.

Kendoha
  • 99
  • 11

2 Answers2

0

You need to go through a new object which doesn't have a circular reference. For example:

public static object ToAnonymousType(this Address address)
{
    var products = address.Products.Select(p => p.ToAnonymousType());
    return new { Id = address.Id, Products=products };
}

public static object ToAnonymousType(this Product product)
{
    return new { Id = product.Id };
}

public JsonResult GetAllOrders()
{
    using(var context = new EFDbContext())
    {
        var addresses = context.Addresses.Include(a => a.Products).ToList().Select(a => a.ToAnonymousType());
        return Json(addresses, JsonRequestBehavior.AllowGet);
    }
}
Craig
  • 474
  • 7
  • 21
  • for some reason I can't call the method `ToAnonymousType()` I'm not that familiar with using `this` in the arguments of a method, so I can't see where the error might be. – Kendoha Oct 21 '15 at 06:53
  • It's using [extension methods](http://www.dotnetperls.com/extension). You don't need to use this - you can just convert to use normal methods, I just find this a bit more readable. – Craig Oct 21 '15 at 08:34
0

Well in this code you don't have the virtual keyword as defined like this:

public virtual IList<Product> Products { get; set; }

So for this part that looks good, don't add the virtual keyword.

If you need to use Include to make the property load it means lazy loading is disabled but just in case you can try to force disabling it

public class EFDbContext: DbContext 
{ 
    public EFDbContext() 
    { 
        this.Configuration.LazyLoadingEnabled = false; 
    } 
}

One more thing is you added [ScriptIgnore] for the Addresses property, but what about adding also it to Products?

public class Address
{
    ....

    [ScriptIgnore]
    public List<Product> Products { get; set; }

    ...
}

You should check this link for more explanation: Loading Related Entities

Jérôme MEVEL
  • 7,031
  • 6
  • 46
  • 78
  • I thougt virtual was neccessary when I enable Lazy loading? Lazy loading alone does not work. My assumption is, that because the software loaded the address anyway it links it up in the reference in product. Why should it not? the data is already there, the link can be made without any further loading of data. – Kendoha Oct 21 '15 at 06:42
  • I only put the `[ScriptIgnore]` in Products because as of right now, I only need the Products in addresses, but later I'll need the references in both directions – Kendoha Oct 21 '15 at 07:05
  • Don't add the virtual keyword as I told you. Lazy loading will LOAD any properties in your object. You don't want that! – Jérôme MEVEL Oct 21 '15 at 13:02