0

I have a .Net Core 2.2 project with EntityFramework and a model with many nested properties either single object or collections.

I've enabled lazy loading and now I want selectively load only the parts of the object tree I'm insterested in in my controller.

When I use .Include() everything is fine until I want to include a collection property and for every item in that collection I want to include a related entity.

Reading the documentation I used this approach:

var mainObj = _db.MyEntityA.AsNoTracking()
            .Include(e => e.MyEntityB)
            .Include(e => e.CollectionOfEntityC.Select((MyEntityC ce) => ce.MyEntityD))

When I run it it gives something like this:

InvalidOperationException: The Include property lambda expression 'e => {from EntityC ce in e.CollectionOfEntityC select [ce].MyEntityD}' is invalid. The expression should represent a property access: 't => t.MyProperty'. To target navigations declared on derived types, specify an explicitly typed lambda parameter of the target type, E.g. '(Derived d) => d.MyProperty'.

So I've tried to add the cast:

var mainObj = _db.MyEntityA.AsNoTracking()
            .Include(e => e.MyEntityB)
            .Include(e => e.CollectionOfEntityC.Select((MyEntityC ce) => ce.MyEntityD))

But nothing changes.

I've tried to use .ThenInclude() this way:

    var mainObj = _db.MyEntityA.AsNoTracking()
            .Include(e => e.MyEntityB)
            .ThenInclude(e => e.Select((MyEntityC ce) => ce.MyEntityD))

With or without the cast, nothing changes.

Finally, reasonably, if I remove that navigation property, it gives me this exception:

Error generated for warning 'Microsoft.EntityFrameworkCore.Infrastructure.DetachedLazyLoadingWarning: An attempt was made to lazy-load navigation property 'MyEntityD' on detached entity of type 'MyEntityCProxy'. Lazy-loading is not supported for detached entities or entities that are loaded with 'AsNoTracking()'.

This same scenario, worked as expected on the EF for the .Net Framework, form which I'm porting to .Net Core.

EDIT:

Exploring the correctly mentioned duplicates, I just wanted to add few more related aspects.

@StriplingWarrior answer below is good; I fixed my code using .ThenInclude() instead of .Select(); now everything builds, but:

  • IntelliSense doesn't help writing the code inside ThenInclude()
  • if I leave AsNoTraking() I must disable the DetachedLazyLoadingWarning
  • if I remove AsNoTracking() EF tracks changes I don't want it to track (I want a readonly snapshot of a complex object)
Andrea Colleoni
  • 5,623
  • 3
  • 27
  • 47
  • [Loading Related Data - Including multiple levels](https://docs.microsoft.com/en-us/ef/core/querying/related-data#including-multiple-levels), e.g. `.Include(e => e.CollectionOfEntityC).ThenInclude(ce => ce.MyEntityD)`. – Ivan Stoev Apr 02 '19 at 14:41
  • Your edit has noting to do with the original question, which *is* a duplicate simply due to invalid `Include` / `ThenInclude` usage. What about the new issue, first is should have been a separate post, and second is duplicate of the EF Core bug asked and answered here https://stackoverflow.com/questions/55369146/eager-loading-include-with-using-uselazyloadingproxies – Ivan Stoev Apr 02 '19 at 19:22

1 Answers1

2

It looks like this is how you're supposed to use ThenInclude:

var mainObj = _db.MyEntityA.AsNoTracking()
        .Include(e => e.MyEntityB)
        .Include(e => e.CollectionOfEntityC)
        .ThenInclude(ce => ce.MyEntityD);
StriplingWarrior
  • 135,113
  • 24
  • 223
  • 283
  • You're right, and coming from EF6 I didn't figure this properly. I've seen duplicates and now it's almost all clear. But I have new issues, maybe you can help: your first option doesn't work but the second does, but only if I disable warning (if I leave AsNoTracking() on) and in any case, every MyEntityD in the result is null. – Andrea Colleoni Apr 02 '19 at 14:49
  • 1
    @AndreaColleoni: After reading the message again I realized my first option was wrong: it would be for when you're using inheritance. I removed it from my answer. If the warning you had to disable is the one mentioned in the link I provided then that's not surprising: it sounds like they have a known issue with Roslyn. WRT MyEntityD being null in the result, I don't think I can help you figure that one out. Maybe you can create a new question showing your mapping and the data? You could also check the ToString() on your query to see how it's trying to get the MyEntityD data. – StriplingWarrior Apr 02 '19 at 15:02
  • 1
    Your help was really precious. I debugged my query and found the issue. I was ThenIncluding an entity only once but i needed two! Thanks! – Andrea Colleoni Apr 02 '19 at 15:13
  • Glad I could help. – StriplingWarrior Apr 02 '19 at 16:29