AutoMapper ProjectTo с многоуровневым DbContext Include()

Мне нужно выполнить многоуровневый запрос Include() во время выбора EF Core. Я использую AutoMapper с ProjectTo<>().

Я указал в сопоставлениях ExplicitExpansion(), что означает, что свойства навигации не будут заполняться автоматически, потому что я хочу иметь возможность выполнять один и тот же запрос несколько раз и одно свойство навигации Include(), но второй раз игнорировать его.

ProjectTo<>() имеет параметры, которые позволяют мне включать свойства навигации в мой выбор, но мне нужно выполнить многоуровневое включение. Это возможно? Такой синтаксис, как Include(e => e.Collection.Select(sc => sc.MyProperty)), в данном случае не работает.

Я пытался использовать Include().ThenInclude() для DbContext, а затем выполнить ProjectTo, но в этом случае ProjectTo переопределяет мои включения, и они игнорируются.

Теперь я не уверен, возможно ли вообще указать ProjectTo, ExplicitExpansion() в сопоставлении и включить многоуровневый?


person Alexey Klipilin    schedule 14.02.2020    source источник
comment
Вам не нужно Include. docs.automapper.org/en/latest/   -  person Lucian Bargaoanu    schedule 14.02.2020
comment
@LucianBargaoanu хорошо ... Мне это нужно, поскольку AutoMapper поддерживает включение для одного уровня внутри метода ProjectTo, почему он не поддерживает включение для второго уровня.   -  person Alexey Klipilin    schedule 14.02.2020
comment
Возможно, прочитайте более внимательно документы. Или проверьте соответствующие тесты в репозитории GitHub.   -  person Lucian Bargaoanu    schedule 14.02.2020


Ответы (2)


Ты пробовал?

dbContext.Entities.ProjectTo<EntityDto>(dest => dest.Collection.Select(item => item.MyProperty));
person Stanisalv Dontsov    schedule 14.02.2020
comment
Я пытался, но в результате получается пустая коллекция. Кажется, недопустимое выражение. - person Alexey Klipilin; 14.02.2020
comment
Теперь это EF Core 5, и этот подход работает - person Alexey Klipilin; 07.05.2021

Я попытался использовать Include().ThenInclude() для DbContext, а затем выполнить ProjectTo, но в этом случае ProjectTo переопределяет мои включения, и они игнорируются.

Это переопределение работает по назначению.


Select() переопределяет Include()

Это явно указано в документации по EF. :

Если вы измените запрос таким образом, чтобы он больше не возвращал экземпляры типа сущности, с которого начался запрос, то операторы включения игнорируются.

В следующем примере операторы включения основаны на Blog, но затем оператор Select используется для изменения запроса для возврата анонимного типа. В этом случае операторы включения не действуют.

Включить

Include() указывает EF загружать некоторые связанные сущности при получении запрошенного набора результатов. Это поведение добавлено к поведению загрузки EF по умолчанию:

var people = db.People.ToList();

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

Добавив Include(), вы существенно расширили поведение загрузки, которое происходит под капотом при перечислении коллекции (в данном случае ToList()).

Выбрать

Select() переопределяет поведение загрузки по умолчанию новым поведением, которое вы определили.

var people = db.People.ToList();

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

Когда вы вызываете Select(), вы, по сути, указываете EF не выполнять поведение загрузки по умолчанию (которое может или не может повлечь за собой дополнительные включения), а вместо этого загружать точно то, что вы указали ( в данном случае person => person.Name).


ProjectTo<>() — это оболочка вокруг Select()

Вы можете думать о ProjectTo<TDestination>() как о своего рода SelectFactory, который генерирует соответствующий оператор Select на основе сопоставления TDestination, настроенного в Automapper.
Под капотом EF по-прежнему выполняет Select(), и, следовательно, такое же поведение, как описано выше. применяется при использовании ProjectTo<>().

Если вы хотите включить дополнительные связанные объекты или какие-либо их свойства, вам необходимо развернуть сопоставление Automapper, а не использовать Include в запросе. Если ваше сопоставление включает дополнительные поля, Automapper соответствующим образом расширит свои базовые Select().

Даже если бы вы могли включить связанные объекты с помощью Include, Automapper все равно проигнорировал бы их, если бы вы никогда не определяли их как сопоставление.

person Flater    schedule 14.02.2020
comment
Я ясно понимаю, что This overriding is working as intended. Итак, все же, почему AutoMapper намеревался поддерживать включение для полей с ExplicitExpansion, но не предполагал поддерживать включение второго уровня. - person Alexey Klipilin; 14.02.2020
comment
@Alexey: вы можете включать связанные объекты, используя соответствующее сопоставление, но вы не можете использовать Include для этого, когда вы также используете ProjectTo() Явное расширение означает, что Automapper автоматически пытается заполнить каждое свойство целевого типа. , что может исключить необходимость явного сопоставления связанных сущностей (если сопоставление может быть выполнено автоматически), но это все равно не меняет того факта, что здесь не следует использовать Include(). - person Flater; 14.02.2020
comment
что меня озадачивает, так это то, что ProjectTo поддерживает логику membersToExpand, но только один уровень. - person Alexey Klipilin; 14.02.2020
comment
@AlexeyKlipilin: Только один уровень звучит как произвольное правило, но это имеет смысл, поскольку он специально предотвращает превращение циклических ссылок в бесконечный цикл. Если Company имеет Company.Employees, а Employee имеет Employee.Company (т. е. другую сторону этого отношения), то вы застряли бы в бесконечном цикле A, создающего экземпляр B, создающего экземпляр A, создающего экземпляр B, создающего экземпляр A... Учитывая высокую вероятность реализации ProjectTo Automapper с помощью EF и навигационные свойства, такие циклические ссылки практически неизбежны. - person Flater; 17.02.2021
comment
но AutoMapper легко обрабатывает циклические ссылки с обычным подходом Include. - person Alexey Klipilin; 17.02.2021
comment
@AlexeyKlipilin: ProjectTo написать конкретный оператор выбора, как правило, больше не связанный с реальными объектами, а с некоторыми произвольными DTO. Хотя EF аккуратно избегает дублирования сущностей для одной и той же записи таблицы, я не уверен, может ли он гарантировать одно и то же для всех пользовательских DTO, особенно когда вы можете вложить целую кучу различных сопоставлений друг в друга. Я не говорю, что это не сработает, я просто не уверен, что это будет полное решение. - person Flater; 18.02.2021