Oleksii Klipilin
Oleksii Klipilin

Reputation: 1936

AutoMapper ProjectTo with DbContext multi level Include()

I need to perform multi level Include() query during EF Core select. I use AutoMapper with ProjectTo<>().

I have specified in mappings ExplicitExpansion() which means navigation properties will not be populated automatically, because I want to have possibility to execute the same query multiple times and one time Include() navigation property but second time ignore it.

ProjectTo<>() method has params which allows me to include navigation properties into my select, but I need to perform multi level include. Is this possible? Such syntax like Include(e => e.Collection.Select(sc => sc.MyProperty)) not working in this case.

I tried to use Include().ThenInclude() for DbContext and then perform ProjectTo, but in that case ProjectTo overrides my includes and they are ignored.

Now I'm not sure if that's possible at all to have ProjectTo, ExplicitExpansion() specified in mapping and have multi level include?

Upvotes: 6

Views: 6153

Answers (2)

Flater
Flater

Reputation: 13823

I tried to use Include().ThenInclude() for DbContext and then perform ProjectTo, but in that case ProjectTo overrides my includes and they are ignored.

This overriding is working as intended.


Select() overrides Include()

This is explicitly mentioned in the EF documentation:

If you change the query so that it no longer returns instances of the entity type that the query began with, then the include operators are ignored.

In the following example, the include operators are based on the Blog, but then the Select operator is used to change the query to return an anonymous type. In this case, the include operators have no effect.

Include

Include() instructs EF to load some related entities when fetching the requested result set. This behavior is added to the default loading behavior of EF:

var people = db.People.ToList();

var peopleWithPets = db.People.Include(person => person.Pets).ToList();

By adding the Include(), you've essentially expanded the loading behavior that happens under the hood when the collection is enumerated (in this case, ToList()).

Select

Select() overrides the default loading behavior with the new behavior you've defined.

var people = db.People.ToList();

var names = db.People.Select(person => person.Name).ToList();

When you call Select(), you essentially instruct EF to not perform its default loading behavior (which may or may not entail additional includes), and instead load exactly what you have specified (in this case, person => person.Name).


ProjectTo<>() is a wrapper around Select()

You can think of ProjectTo<TDestination>() as a sort of SelectFactory which generates the appropriate Select statement based on the TDestination mapping that has been configured in Automapper.
Under the hood, EF still executes a Select(), and therefore the same behavior as described above applies when using ProjectTo<>().

If you want to include additional related entities or any of their properties, you need to expand the Automapper mapping, as opposed to using Include on the query. If your mapping includes additional fields, Automapper will expand its underlying Select() accordingly.

Even if you had been able to include the related entities using Include, Automapper would ignore them anyway if you never defined them as a mapping.

Upvotes: 8

Stanislav Dontsov
Stanislav Dontsov

Reputation: 1741

Did you try?

dbContext.Entities.ProjectTo<EntityDto>(dest => dest.Collection.Select(item => item.MyProperty));

Upvotes: 1

Related Questions