При проекции в DTO, если проекция происходит в IEnumerable, то entity framework не будет вытягивать эти данные при запросе, а вытянет после при обращении к этой коллекции, поэтому и возникает n+1 запрос. Чтобы избежать этого, можно поменять тип свойства на массив, тогда при проекции automapper сам сделате ToArray:
public class Dto{
public SomeCollection[] arr;
}
public class Entity{
SomeEntity[] SomeCollection{get;set;}
}
public class AutoMapperProfile:Profile{
AutoMapperProfile(){
CreateMap<Entity,Dto>().ForMember(x=>x.arr,x=>x.SomeCollection);
}
// using
var q = query.ProjectTo<Dto>();
// автомаппер сам сделает :
// expression из q - dbSet.Select(x=>new Dto(){arr = x.SomeCollection.ToArray()}
//
}
Если сделать коллекцию массивом, то будет сделан всего один дополнительный запрос(join между Entity и SomeEntity и orderby по Entityid результатов, после этого сопоставляется (в c# коде) по EntityId сджойненные записи и результирующая последовательность). Таким образом делается все 2 запроса, первый результирующий, второй тянущий сджойненные SomeEntity и Entity.
А что будет если arr будет иметь тип IEnumerable, тогда (без указания явного ToList или ToArray в маппинге), автомаппер не будет достраивать вызов этих методов, и вернется EnumerableAdapter(entity framework core 2.2). Которая при вызове GetEnumerator, будет делать запрос