Оптимизация коррелированных подзапросов с помощью Automapper - PullRequest
0 голосов
/ 17 сентября 2018

Обновление: Automapper применяет это автоматически в простых случаях, так как уже добавляет ToList().Проблема, с которой я столкнулся, заставила меня открыть этот вопрос, оказывается более сложной ( член SoftwareIds является виновником N+1. См. this .).


В EF Core 2.1 мы получили поддержку добавления ToList() в подзапрос LINQ для буферизации результатов и избежания N + 1 запросов к базе данных.( Docs ) Это прекрасно работает с простыми запросами LINQ к DbContext.

Однако, если у меня есть профиль Automapper, который приводит к N + 1 запросам:

    public MyMappingProfile() =>
        CreateMap<MyEntity, MyDto>().ForMember(e => e.MyCollectionProp, o => o.MapFrom(l => l.MyCollectionPropMany.Select(la => la.MyCollectionEntity)))

При добавлении ToList() выдается исключение:

    public MyMappingProfile() =>
        CreateMap<MyEntity, MyDto>().ForMember(e => e.MyCollectionProp, o => o.MapFrom(l => l.MyCollectionPropMany.Select(la => la.MyCollectionEntity).ToList()))

System.NotSupportedException: «Не удалось проанализировать выражение» MyDto.MyCollectionPropMany.Select (la => la.MyCollectionEntity) .ToList () ':Эта перегрузка метода System.Linq.Enumerable.ToList в настоящее время не поддерживается.

Есть ли способ включить буферизацию подзапроса в профиле Automapper?

Модели:

public class MyEntity
{
    public int Id { get; set; }
    public ICollection<MyCollectionPropMany> MyCollectionPropManys { get; set; }
    ...
}

public class MyCollectionPropMany
{
    public int MyEntityId { get; set; }
    public MyEntity MyEntity { get; set; }
    public int MyCollectionPropId { get; set; }
    public MyCollectionProp MyCollectionProp { get; set; }
}

public class MyCollectionProp
{
    public int Id { get; set; }
    public ICollection<MyCollectionPropMany> MyCollectionPropManys { get; set; }
    ...
}

public class MyDto
{
    public int Id { get; set; }
    public IEnumerable<MyCollectionPropDto> MyCollectionPropDtos { get; set; }
    ...
}

public class MyCollectionPropDto
{
    public string Name { get; set; }
    ...
}

Automapper v7.0.1

Реальный сценарий (я пытался упростить / сделать универсальный для SO): Source В этом реальном примере Languagesи Tags членов через «многие ко многим» в настоящее время генерируют N + 1 запросов.

1 Ответ

0 голосов
/ 20 сентября 2018

Оказывается, что AutoMapper иногда автоматически добавляет ToList / ToArray к выражению проекции при отображении перечислимых типов, иногда нет.

Правило выглядит так:следующим образом.Если перечислимый тип назначения может быть напрямую назначен из исходного типа выражения, AutoMapper использует непосредственно исходное выражение.Другими словами, если следующее присваивание допустимо (псевдокод):

dst.Member = src.Expression;

В этом случае вы должны включить ToList или не включить в выражение отображения (таким образом, согласитесь на EF)Оптимизация коррелированных запросов ядра).

Во всех остальных случаях AutoMapper при необходимости выполняет сопоставление перечислимых элементов, а затем добавляет либо ToArray, либо ToList.Отказ от участия невозможен.

Вскоре, если целевой перечислимый тип элемента, если Dto (требует сопоставления), не включает ToList в исходное выражение LINQ, если онотип примитива или сущности, do include ToList, чтобы избежать N + 1 запросов.Все это применимо, если тип целевой коллекции IEnumerable<T>.Любой другой производный тип коллекции, например IReadOnlyCollection<T>, IReadOnlyList<T>, ICollection<T>, IList<T>, List<T>, T[] и т. Д., Будет автоматически обрабатываться AutoMapper в случае, если исходное выражение возвращает IEnumerable<TSource>.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...