Include property but exclude one of that property's properties

tobloef

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 a product from the database and include its Category property.

In my CategoryControllerI have 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 this works as intended, I get the category, its parent, its products and its sub-categories. But when I send a GET request to the ProductController I don't want to include all the products in the category of the requested product, I just need the basic information about that category.

So, how can I make GetProducts() return the products in the database, including the Category property of each product, but excluding the Products list property of the category, still keeping the other properties like id, title and so on?

Thank you.

Gert Arnold

As said in the comments, the first step is to disable lazy loading. You can either do that by removing the virtual modifier from the collection properties, which is permanent, or by disabling it per context instance, which is temporary:

context.Configuration.ProxyCreationEnabled = false;

(disabling proxy creation also disables lazy loading, but keeps the generated objects more light-weight).

In disconnected scenarios, like Web AIP2 (as you originally tagged the question), people often prefer to disable lazy loading by default, because of this serializer-lazy-loading cascade.

However, you can't stop Entity Framework from executing relationship fixup. Loading a Productattaches it to the context. Include()-ing its categories attaches those to the context and EF populates their Products collections with the attached product, whether you like it or not.

You can somewhat reduce this effect by fetching the products with AsNoTracking (which prevents entities to get attached, i.e. change-tracked):

return db.Products.AsNoTracking()
         .Include(p => p.Category);

Now categories will only have their Products filled with the Product of which they are the category.

By the way, in disconnected scenarios, also using AsNoTracking is preferred. The entities won't ever be saved by the same context instance anyway and it increases performance.

Solutions

  • Return DTOs, not entity types

By using DTO objects you take full control over the object graph that will be serialized. Lazy loading won't surprise you. But yeah, the amount of required DTO classes can be overwhelming.

  • Return anonymous types.

This will raise some eyebrows because we should never return anonymous types from methods, right? Well, they leave an action method as a Json string, just as named types, and the javascript client doesn't know the distinction. You might say that it only brings the weakly typed javascript environment one step closer. The only thing is that a named DTO type serves as a data contract (of sorts) and anonymous types can be changed (too) easily and break client-side code. But we always unit-test everything, do we? Hmm, it's a viable option in smaller development teams.

  • Tweak the serializer.

You can tell the Json.Net serializer to ignore reference loops. Using JsonConvert directly, it looks like so:

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 serialize the categories with empty Products arrays ("Products": []), because Product - Category - Product is a reference loop.

In Web API there are several ways to configure the built-in Json.Net serializer, you may want to do this per action method.

Personally, I prefer using DTOs. I like to be in control (also over the properties that cross the wire) and I don't particularly like to rely on a serializer to solve for me what I neglected to do.

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

java properties file using multiple lines for one property

How to overwrite one property in .properties without overwriting the whole file?

Exclude property from type

Property include/exclude on Kotlin data classes

Changing one property in a variable triggers subcriptions to other properties. RxSwift

How to exclude object property

Select one property from multiple expanded properties

EF - Load properties of child property with single .Include() possible?

Assigning one of many values to object's property

Exclude property from Template

How to update other properties of an object if one property changes?

Only update one property of the collection's field

How to merge properties inside object into one property?

How to assign additional properties with one property in Angular

Exclude property using mvn versions:update-properties with excludesList

Include/Exclude Spring Bean Property List Value Node

Grails - include id property when calling .properties

Custom Property with Sub-Properties in Visual Studio's Properties Pane

Changing one property in a Sass variable with multiple properties

Include file content into property?

React: Updating one state property removes other states properties in the state

Display properties of Model based on one Model property

Changing one property in multidimensional array changes other array's properties too

Merge similar properties in a object into one property

How to copy properties from one Bean to Another and ignore nested Object's property

How to assign an object's property to another one of it's properties in reactjs?

Exclude property in checking empty properties

Cannot have multiple properties when having one property's name passed via generic

List of dictionaries to list of one property's values

TOP Ranking

HotTag

Archive