How to make EF Core Include() not keep track of entities?

.net-core c# entity-framework entity-framework-core

Question

TLDR: I would like to know if using different "include logics" for one entity type in a single query possible in EF core.

Edit: Just to be clear, in the title I said keeping track of entities because I think that's what EF does, but .AsNoTracking() does nothing useful here, before anyone suggests.

The problem at hand is on a relatively small React application backed with an ASP.NET Core web api application. What I want to accomplish is, when I call api/parents, I want the application to give me a json that looks like this:

[
  {
    "id": "parentid1",
    "extraProperty": "value",
    "children": [
      {
        "id": "childid1",
        "parent": {
          "id": "parentid1",
          "extraProperty": "value"
        }
      }
    ]
  }
]

My setup looks like this:

EF query:

(from p in _context.Parents
 select p)
 .Include(p => p.Children)
     .ThenInclude(c => c.Parent)
 .ToList();

After that I have AutoMapper mapping the entity to the dto, there is not much going on there. I also use the default Json Serializer (Newtonsoft) for the application. It is configured with SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore.

With this setup the api call gives back this response:

[
  {
    "id": "parentid1",
    "extraProperty": "value",
    "children": [
      {
        "id": "childid1"
      }
    ]
  }
]

Which as you can see "ignores" the self reference of the parent.

The solution I came up with was, I should configure Newtonsoft to "serialize" reference loops, and I tried it. And it throws, because the EF query provided above returns an entity list that looks like this:

[
  {
    "id": "parentid1",
    "extraProperty": "value",
    "children": [
      {
        "id": "childid1",
        "parent": {
          "id": "parentid1",
          "extraProperty": "value",
          "children": [
            {
              "id": "childid1",
              "parent": {
                "id": "parentid1",
                "extraProperty": "value"
                ...
              }
            }
          ]
        }
      }
    ]
  }
]

If you look at my Include calls, it explicitly says that for my child objects I only want the parent object and no other references inside that parent object.

To my best guess, EF uses the initial setup .Include(child).ThenInclude(parent) when it encounters with any Parent object for that query. So when it finds a Parent object in the Child object, instead of using no includes (which I don't have any after the ThenInclude) it uses .Include(child).ThenInclude(parent).

I don't want to solve this problem through hacks with either the mapper or the serializer if I'm not have to. I would like to know if what I am seeking is possible: To use different "include logics" for one entity type in a single query.

1
0
3/19/2019 11:27:31 AM

Popular Answer

The ThenInclude(parent) call is redundant since you're starting with the parent - the materialized children will already have their Parent property populated. You would need to customize the serialization as @IvanStoev stated in the first comment.

The alternative is to query for children by parent then project the result you want:

var parents = _context.Children
    .Include( c => c.Parent )
    .GroupBy( c => c.Parent )
    .ToArray()
    .Select( g => 
        {
            // assign children to g.Key (the parent object)
            g.Key.Children = g.ToArray(); // I don't know type of `Children` property
            // select parent
            return g.Key;
        } );
1
3/19/2019 1:03:12 PM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow