As stated in the comments, the first step is to disable lazy loading . You can do this by removing the virtual modifier from the properties of the collection, which is permanent, or by disabling it for each instance of the context that is temporary:
context.Configuration.ProxyCreationEnabled = false;
(disabling proxy creation also disables lazy loading, but keeps the generated objects easier). A.
In disabled scripts such as Web AIP2 (as you already mentioned the question), people often prefer to disable lazy loading by default because of this serializer-lazy loading cascade.
However, you cannot stop the Entity Framework from performing a relationship fix . Loading a Product binds it to context. Include() using its categories, binds them to the context, and EF fills its Products collections with the attached product, whether you like it or not.
You can slightly reduce this effect by extracting products using AsNoTracking (which does not allow you to bind objects to the connection, i.e. track changes):
return db.Products.AsNoTracking() .Include(p => p.Category);
From now on, categories will only have Products filled with Product , of which they are a category.
By the way, in disabled scenarios, it is preferable to use AsNoTracking . Entities will never be stored in the same instance of context and increase performance.
Decision
- Returns DTO, not entity types
Using DTO objects, you have complete control over the object graph that will be serialized. Lazy loading will not surprise you. But yes, the amount of DTO classes required can be overwhelming.
This will cause some eyebrows because we should never return anonymous types from methods, right? Well, they leave the action method as a Json string, just like named types, and the javascript client does not know the difference. You could say that this only brings the weakly typed javascript environment one step closer. The only thing that the named DTO type serves as a contract for data (views), and anonymous types can be easily changed (also) and break the client code. But we always check everything, right? Hmm, this is a viable option in small development teams.
You can say that the Json.Net serializer ignores link loops. Using JsonConvert , it looks like this:
var products = db.Products.AsNoTracking().Include(p => p.Category); var setting = new JsonSerializerSettings { Formatting = Newtonsoft.Json.Formatting.Indented,
In combination with AsNoTracking() this will result in the serialization of categories with empty Products ( "Products": [] ) arrays, since Product - Category - Product is a reference loop.
There are several ways to configure the Json.Net embedded serializer in the Web API; you can do this for each action method .
Personally, I prefer to use DTO. I like to control (also over the properties that cross the wire), and I don't particularly like relying on the serializer to decide for me what I neglected to do.