4

This is the code I am using to handle a GET request in my "Works Orders" controller:

    // GET: api/WorksOrders/5
    /// <summary>
    /// Fetches the Works Order with corresponding ID (pkOrderItemID)
    /// </summary>
    [Authorize]
    public OrderItem Get(int id)
    {
        using (var entities = new customappsEntities())
        {
            return entities.OrderItems.FirstOrDefault(e => e.pkOrderItemID == id);
        }
    }

When this is run I get this error message in the JSON response:

 "Error getting value from 'OrderItemDepartments' on 'System.Data.Entity.DynamicProxies.OrderItem_501562E50E13B847D4A87F7F2DEC7C8CEDAF127355CB4FC30E12653275CE6412'.",

I can fix this by adding

entities.Configuration.ProxyCreationEnabled = false;

This returns the entire table but has a set of empty arrays at the bottom for example the JSON formatted response looks like:

{
  "$id": "1",
  "pkOrderItemID": 271,
  "StartedOn": "2015-01-01T00:00:00",
  "CompletedOn": "2014-10-15T00:00:00",
  "Costs": [],
  "Dispatches": []
}

The arrays appear to be foreign key relationships in the database and I believe they return empty because it is stuck in a self-referencing loop (I was able to get that error message a few times but I haven't been able to recreate it since). I've tried adding

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.Re‌​ferenceLoopHandling = ReferenceLoopHandling.Ignore;

From: Self referencing loop detected - Getting back data from WebApi to the browser

And many other solutions from the same or similar threads but it didn't solve the issue. All of my data classes are auto-generated by Entity Framework so I can't modify them, maybe this is the wrong way to have a web API set up?

Any help to figure out why the arrays return empty will be appreciated. Thank you.

Update

Here's a screenshot of the DataModel Diagram to show the fk relationship

Here's a screenshot of the DataModel Diagram to show the fk relationship

Community
  • 1
  • 1
Jaanko
  • 43
  • 6

2 Answers2

2

Do not return anything other than POCOs or anonymous objects via JSON serializing. Entity Framework objects have a huge overhead you do not want to see and often have circular relationships that the serializer cannot cope with (and you do not want, even if you configure it to allow them).

Create a POCO or return a select:

[Authorize]
public MyOrderItem Get(int id)
{
    using (var entities = new customappsEntities())
    {
        var item = entities.OrderItems.FirstOrDefault(e => e.pkOrderItemID == id);
        return new MyOrderItem()
        {
             StartedOn = item.StartedOn,
             ...
        };
    }
}

The key to correctly using JSON is to return only what you need across the line.

Update

As the item contains child items, you need to ensure you have POCOs for those too:

e.g.

var myOrderItem = new MyOrderItem()
{
     StartedOn = item.StartedOn,
     Costs = new List<MyCost>();  // <=== This can also go in the default constructor
     ...
};
foreach (var cost in item.Costs)
{
     myOrderItem.Costs.Add(new MyCost()
     {
         Value = cost.Value
         ...
     });
}
return myOrderItem;

Another update

That is very odd. It certainly should give you your records. You can also try an .Include() with .Select() and FirstOrDefault() instead of just FirstOrDefault() to guarantee the related records are pre-loaded.

e.g.:

entities.OrderItems.Include(x=>x.Costs).Select(x=>x).FirstOrDefault();
iCollect.it Ltd
  • 92,391
  • 25
  • 181
  • 202
  • Thanks for the response. I've added this but I still get empty arrays. I added a break point and it says count = 0 for item.Costs which is an ICollection if that helps – Jaanko Dec 20 '16 at 10:40
  • You added a breakpoint *where*? Can you show how you are trying to populate your new POCOs with data? – iCollect.it Ltd Dec 20 '16 at 11:46
  • Hi, I've added the breakpoint on the return line so I could check the values that were being fetched. Here is how I'm currently trying to populate it: `return new OrderItem() { pkOrderItemID = item.pkOrderItemID, StartedOn = item.StartedOn, CompletedOn = item.CompletedOn, Costs = item.Costs, Dispatches = item.Dispatches };` I have also tried Costs with .ToArray() and .ToList(). Sorry for poor comment formatting. – Jaanko Dec 20 '16 at 12:06
  • I am assuming costs is another table? You need a POCO for those objects too. I will add an example but I have to guess at your object structures. – iCollect.it Ltd Dec 20 '16 at 12:14
  • Thanks for your help. Costs is indeed another table and they are linked with a foreign key stored in the costs table(The Entity Framework shows the link in the diagram that it auto-generated so I assume it is set up correctly). I have added the code from your edit but the problem is that `item.Costs` is returning empty so the foreach loop doesn't run. – Jaanko Dec 20 '16 at 12:41
  • If there is related data, with a foreign key correctly set, then it should not be empty. EF will load them on demand if you don't use an `include`. Please check your database and see if there are related Costs records for that primary Item record. – iCollect.it Ltd Dec 20 '16 at 13:54
  • Thanks again for the help, I have checked the database and it definitely has data for the record. I guess it's back to the drawing board at the minute with this then! Just incase you have any more ideas I've attached a screenshot onto the original post to show the relationship. Cheers. – Jaanko Dec 20 '16 at 14:29
  • That is very odd. It certainly should give you your records. You can also try an `.Include()` with `.Select()` and `First()` instead of `FirstOrDefault()` to guarantee the related records are preloaded. Added example above – iCollect.it Ltd Dec 20 '16 at 14:36
  • Adding the `.Include` and `.Select` has sorted it! I really appreciate all the help you've put in. Thank you. – Jaanko Dec 20 '16 at 14:59
  • Glad that worked. I still cannot think of a reason it did not work before though. – iCollect.it Ltd Dec 20 '16 at 15:04
-1

Add Newtonsoft Json Library and return the result like this

JsonSerializerSettings jss = new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore };
    return JsonConvert.SerializeObject(result, Formatting.Indented, jss);