Include property, but exclude one of property properties

Let's say I have a method like this in one of my controllers:

[Route("api/Products")] public IQueryable<Product> GetProducts() { return db.Products .Include(p => p.Category); } 

Using this, I can get the product from the database and enable its Category property.

In my CategoryControllerI there is this method:

 [Route("api/Categories")] public IQueryable<Category> GetCategories() { return db.Categories .Include(c => c.Parent) .Include(c => c.Products) .Include(c => c.SubCategories); } 

When I send a GET request to the CategoryController, it works as intended, I get the category, its parent, its products and its subcategories. But when I submit a GET request to ProductController, I don’t want to include all the products in the requested product category, I just need some basic information about this category.

So, how can I get GetProducts () to return products in the database, including the Category property for each product, but excluding the product list property in the category, while preserving other properties such as id, title, etc.?

Thanks.

+6
source share
1 answer

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.

  • Returns anonymous types.

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.

  • Tune the serializer.

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, // Just for humans ReferenceLoopHandling = ReferenceLoopHandling.Ignore }; var json = JsonConvert.SerializeObject(products, setting); 

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.

+5
source

All Articles