AutoMapper Project(). To() и сортировка дочерней коллекции - программирование
Подтвердить что ты не робот

AutoMapper Project(). To() и сортировка дочерней коллекции

У меня есть граф объектов, который я загружаю из базы данных, используя EF CodeFirst и AutoMapper в DTO: -

public class Foo
{
  public int Id { get; set; }
  public virtual ICollection<Bar> Bars { get; set; }
}

public class Bar
{
  public int Id { get; set; }
  public int FooId { get; set; }
  public virtual Foo Foo { get; set; }

  public string Name { get; set; }
  public int SortOrder { get; set; }
}

public class FooDto
{
  public IEnumerable<BarDto> Bars { get; set; }
}

public class BarDto
{
  public string Name { get; set; }
  public int SortOrder { get; set; }
}

Мои сопоставления выглядят так: -

mapper.CreateMap<Foo, FooDto>();
mapper.CreateMap<Bar, BarDto>();

До сих пор так хорошо. Я могу захватить сущности из моего контекста и выполнить проект в DTO: -

var foos = context.Foos.Project().To<FooDto>();

То, что я не могу сделать с этим подходом, однако, сортирует Bars по их SortOrder внутри IQueryable.

Если я попробую: -

mapper.CreateMap<Foo, FooDto>()
  .ForMember(
    x => x.Bars
    opt => opt.MapFrom(src => src.Bars.OrderBy(x => x.SortOrder)));
mapper.CreateMap<Bar, BarDto>();
var foos = context.Foos.Project().To<FooDto>();

Я получаю исключение: -

System.InvalidOperationException: Sequence contains no elements
  at System.Linq.Enumerable.First[TSource](IEnumerable`1 source)
  at AutoMapper.MappingEngine.CreateMapExpression(Type typeIn, Type typeOut)
  ...

Похоже, это связано с https://github.com/AutoMapper/AutoMapper/issues/159 - хотя я уже использую сложный тип для дочерней коллекции. Я думаю, что CreateMapExpression не поддерживает OrderBy в дочерних коллекциях?

Если я не использую .Project(). Для() я могу легко сортировать дочернюю коллекцию: -

var model = context.Foos.Select(x => new FooDto()
{
  Bars = x.Bars.OrderBy(y => y.SortOrder)
});

но затем мне нужно повторить отображение везде, где я хочу его использовать, победив цель использования AutoMapper.

Любопытно: -

1) Я могу выполнять другие (более сложные?) операции над дочерней коллекцией и сглаживать их в моем родительском DTO без проблем: -

mapper.CreateMap<Foo, FooDto>()
  .ForMember(
    x => x.AllBarsHaveAName,
    opt => opt.MapFrom(src =>
      src.Bars.All(x => x.Name != null)));

2) Я могу Mapper.Map<FooDto>(foo); в памяти просто отлично, и он не сортирует бары без проблем.

Можно сортировать дочернюю коллекцию на уровне IQueryable при использовании .Project(). To()?

4b9b3361

Ответ 1

Закончено изменение исходного кода AutoMapper для поддержки этого сценария. Надеемся, что предлагаемое исправление будет принято, но пока вы можете увидеть подробности: -

https://github.com/AutoMapper/AutoMapper/pull/327

Ответ 2

Интересно, является ли сортировка баров внутри Queryable во время сопоставления лучшим местом для этого? Не помещал ли этот вид на карту, чтобы сортировать ее в ненадлежащее время?

Например, что, если вы просто хотели узнать, содержит ли коллекция Bars 1 элемент? призвание FooDto.Bars.Any() все равно приведет к сортировке в базе данных.

Возможно, было бы лучше иметь другое свойство в FooDTO (игнорируется в сопоставлении), которое выглядит так:

    public IEnumerable<BarDto> SortedBars
    {
        get
        {
            if (Bars == null)
                return null;

            return Bars.OrderBy(x => x.SortOrder).ToList();
        }
    }