Коллекция сущностей, связанных только с идентификатором EF core + Automapper, как?

У меня есть простая проблема: я хотел бы, чтобы одна из конечных точек RESTful обслуживала ресурс DTO (автоматически сопоставленный) со связанными с ним ресурсами только в качестве их идентификаторов. Однако, похоже, нет никакого способа реализовать это без загрузки целых (и тяжелых) связанных объектов. Рассмотрим следующую (сначала БД) примерную модель:

public partial class Blog
{
    public int Id { get; set; }
    public string Url { get; set; }
    public virtual ICollection<Post> Posts { get; set; }
}

public partial class Post // some heavy entity
{
    public int Id { get; set; }
    public string Content { get; set; }
    // other properties
}

и соответствующий DTO

// api/v1/blogs serves collection of following type
public class BlogSlimDto
{
    public int Id { get; set; }
    public string Url { get; set; }
    public int[] PostIds { get; set; }
}

простым решением было бы извлечь все связанные Post из базы данных и отбросить все данные, кроме идентификаторов, но это может быть неэффективным или даже невыполнимым в зависимости от размера связанного объекта Post:

var result = ctx.Blogs.Include(blog => blog.Posts) //fecth everything and discard it on next line
    .Select(blog => _mapper.Map<BlogSlimDto>(blog)); 
    // simply use a profile that discards Posts but keeps their Ids, e.g.
    // .forMember(dto => dto.PostIds, opt => opt.MapFrom(db.Posts.Select(p => p.Id)))

есть похожий вопрос, который предлагает решение используя анонимные типы, однако это совсем не работает с Automapper:

var result = ctx.Blogs.Select(blog => new {
   blog.Id,
   blog.Url,
   PostIds = blog.Posts.Select(b => b.Id),
}).Select(ablog => _mapper.Map<BlogSlimDto>(ablog)); //throws, no mapping and such mapping cannot be defined

Приведенный выше код будет сгенерирован во время выполнения, поскольку сопоставление Automapper не определено. Хуже того, его нельзя определить, потому что существует нет поддержки анонимных типов в Automapper. Более того, решения с поочередным «ручным» назначением свойств, как правило, сложно поддерживать.
Существует ли альтернативное решение, которое позволяло бы запрашивать EF без извлечения всех связанных сущностей, позволяя при этом автоматически сопоставлять результат с BlogSlimDto?


person wondra    schedule 27.12.2019    source источник
comment
Почему вы настаиваете на использовании automapper... есть только 3 свойства?   -  person Milney    schedule 27.12.2019
comment
@Milney, в листинге кода всего 3 свойства, было бы излишне раздувать сообщение, чтобы включить их все - вам просто нужно доверять встроенным комментариям, которых достаточно, чтобы оправдать работу (текущее время отклика 30 с + должно).   -  person wondra    schedule 27.12.2019
comment
Я понимаю, что вы не хотите запрашивать все свойства libked объекта, я имею в виду, что просто введите select() вручную вместо использования automapper   -  person Milney    schedule 27.12.2019
comment
Причина @Milney заключается в согласованности и поддерживаемом коде / передовых методах - приложение имеет около 40 конечных точек, поэтому было бы запутанно и подвержено ошибкам использование ручного сопоставления в этом и Automapper в остальных. Полностью согласен с тем, что в зависимости от масштаба иногда не стоит затрачивать усилия на настройку автоматического сопоставления.   -  person wondra    schedule 27.12.2019


Ответы (1)


Вы можете использовать запрашиваемые расширения: https://docs.automapper.org/en/stable/Queryable-Extensions.html

var configuration = new MapperConfiguration(cfg =>
    cfg.CreateMap<OrderLine, OrderLineDTO>()
    .ForMember(dto => dto.Item, conf => conf.MapFrom(ol => ol.Item.Name)));

public List<OrderLineDTO> GetLinesForOrder(int orderId)
{
  using (var context = new orderEntities())
  {
    return context.OrderLines.Where(ol => ol.OrderId == orderId)
             .ProjectTo<OrderLineDTO>().ToList();
  }
}

Замена Item и OrderLine вашим сообщением и блогами

person Milney    schedule 27.12.2019
comment
Звучит интересно, буду изучать. - person wondra; 27.12.2019