0

I am getting a self Referencing issue with EF and I'm trying to over come it but still allow the Service to be able to perform a GET passing in {[FromODataUri] int key} a key and return an IQuerable Obj to get the Expanded tables if necessary. Below is a slimmed down version of the tables. Any suggestions on how to handle the situation.

public class People
{
 public int PeopleId {get;set;}
 public string PeopleName {get;set;}

 public int? ProductId{get;set;}

 public virtual Product Product{get;set;}
}

The ProductId is a PK in Product but its not required. As per the convention it doesn't have to be Decorated with the PK DataAnnotation overide.

public class Product
{
   public Product()
   {
      PeopleCollection = HashSet<People>();
   }
   public int ProductId {get;set;}
   public string ProductName {get;set;}
   public virtual ICollection<People> Peoples{get;set;}
}
Angel Silva
  • 365
  • 2
  • 5
  • 16
Joe Ricklefs
  • 584
  • 4
  • 24
  • WebAPI is serializing your response, but Product references People, which references a product, which references people, which references product. You may need to reconsider if having `Product` on your `People` class is necessary, or perhaps map your entities to a DTO which avoids this circular reference. – Tom May 17 '17 at 15:03
  • I should add, this is probably a symptom of having `lazy-loading` enabled, as the serializer is looking into the properties and then loading the entity. – Tom May 17 '17 at 15:04
  • Possible duplicate of http://stackoverflow.com/questions/19467673/entity-framework-self-referencing-loop-detected – Tom May 17 '17 at 15:05

2 Answers2

0

In this case I recommend using DTO's or using anonymous objects, for example:

public IHttpActionResult Get() {
    var response = db.YourTable.Select(x=>new{
       x.PeopleId,
       x.PeopleName,
       x.ProductId,
       Product= new {
         x.ProductId,
         x.ProductName  
       }
    }).toList();
    return Ok(response);
}

Thats how I would do it with anonymous objects, If you want to use DTO's you just need to map them, hope this is what you are looking for.

For just a specific id:

public IHttpActionResult Get(int id) {
    var response = db.YourDb.Select(x=>new{
       x.PeopleId,
       x.PeopleName,
       x.ProductId,
       Product= new {
         x.ProductId,
         x.ProductName  
       }
    })Where(x=>x.PeopleId == id).toList();
    return Ok(response);
}

Note, this method is using query string parameters

Angel Silva
  • 365
  • 2
  • 5
  • 16
  • Wouldn't this always return all the associated Tables? for example if I flip this arround and I do a get on the product `code var rslt = db.Products.Select(x=> new { x.ProductId, x.ProductName, new List(){ y = y.PeopleId, y.PeopleName, y.ProductId};code` would bring back all the Product and All the People in each call? – Joe Ricklefs May 17 '17 at 16:06
  • vs Using Odata Query where I could just make the GET to Product it would only return my Products and It wouldn't return any associated data Unless I added the ?$expand=People to my QueryString – Joe Ricklefs May 17 '17 at 16:12
  • @JoeRicklefs yes it will, if you want to search for an specific id add before .toList() this: Where(x=>x.PeopleId == key).toList(), – Angel Silva May 17 '17 at 16:12
  • That would work except this service is queried through the URL and I don't know what Fields/ or Conditions that will be sent over. So the solution needs to be more flexible. If a user sends over just a Get to api/People The services will only return the People information but if they send over Api/People?$expand=Product the Result would include the people and the associated product. – Joe Ricklefs May 17 '17 at 16:21
  • @JoeRicklefs Oh I see, well a found a very good guide by microsoft: [link](https://learn.microsoft.com/en-us/aspnet/web-api/overview/odata-support-in-aspnet-web-api/supporting-odata-query-options) – Angel Silva May 17 '17 at 16:23
  • That doesn't help with the original issue. I know how to query data using Odata, but EF is coming up with the Self Referenceing loops issue – Joe Ricklefs May 17 '17 at 17:43
0

I figured this out after some time. The self referencing issue will come up if you are Inheriting from APIController but if you switch to inherit from ODataController everything works.

So

 public class MyController : ApiController
    {
     ..... Bunch of code here
    }

To

public class MyController : ODataController
    {
     ..... Bunch of code here
    }
Joe Ricklefs
  • 584
  • 4
  • 24