.Net Core Automapper с проблемой отношений «многие ко многим»

Я новичок в Automapper, и у меня проблема с отображением моих классов, которые имеют отношения «многие ко многим». Я думаю, мне нужно что-то вроде вложенного отображения, но документация не кажется мне ясной. Я знаю, что делаю что-то не так с конфигурацией AutoMapper, но не могу понять, что не так.

Когда я добавляю новую книгу в базу данных, я хотел бы получать информацию о книге и авторах, написавших книгу. (ожидаемый результат JSON ниже)

У меня есть следующие классы:

    public class Book
    {
        public int BookId { get; set; }
        public string Title { get; set; }
        public string ISBN { get; set; }
        public string Category { get; set; }
        public int PublisherId { get; set; }
        public Publisher Publisher { get; set; }
        public ICollection<BookAuthor> BooksAuthors { get; set; }
}

    public class Author
    {
        public int AuthorId { get; set; }
        public string AuthorName { get; set; }
        public string AuthorLastName { get; set; }
        public ICollection<BookAuthor> BooksAuthors { get; set; }
    }

    public class BookAuthor
    {
        public int BookId { get; set; }
        public Book Book { get; set; }

        public int AuthorId { get; set; }
        public Author Author { get; set; }
    }

DTO

    public class BookForNewDto
    {
        public string Title { get; set; }
        public string ISBN { get; set; }
        public string Category { get; set; }
        public int PublisherId { get; set; }
        public ICollection<AuthorForNewBookDto> Authors { get; set; }
    }

    public class AuthorForNewBookDto
    {
        public int AuthorId { get; set; }
    }

Букконтроллер (AddBook)

        public async Task<IActionResult> AddBook([FromBody] BookForNewDto bookForNewDto)
        {
            var bookToCreate = _mapper.Map<Book>(bookForNewDto);

            var bookToReturn = _mapper.Map<BookForNewDto>(bookToCreate);

            var bookToAdd = await _context.Books.AddAsync(bookToCreate);

            await _context.SaveChangesAsync();

            var bookIdToAdd = bookToCreate.BookId;
            var authorIdToAdd = bookForNewDto.Authors.Select(aa => aa.AuthorId).ToList();

            foreach (var item in authorIdToAdd)
            {
                var bookAuthorToAdd = new BookAuthor()
                {
                    BookId = bookIdToAdd,
                    AuthorId = item
                };
                var newBookAuthor = await _context.BooksAuthors.AddAsync(bookAuthorToAdd);
                await _context.SaveChangesAsync();
            }

            return Ok(bookToReturn);
        }

AutoMapper Profiler // Я прикрепил только раздел, касающийся добавления новой книги.

public class AutoMapping : Profile
    {
        public AutoMapping()
        {
               
            CreateMap<BookForNewDto, Book>();
            CreateMap<Book, BookForNewDto>()
                .ForMember(dto => dto.PublisherId, opt => opt.MapFrom(x => x.PublisherId))
                .ForMember(dto => dto.Authors, c => c.MapFrom(c => c.BooksAuthors));

            CreateMap<BookForNewDto, AuthorForNewBookDto>()
                .ForMember(dto => dto.AuthorId, opt => opt.MapFrom(x => x.Authors.Select(aaa => aaa.AuthorId)));
        }

Мой вопрос: как мне настроить AutoMapper Profiler, чтобы получить приведенный ниже результат в формате JSON? У меня проблема с авторами. Я по-прежнему получаю пустой список.

{
    "title": "sample title",
    "isbn": "123-41-5-12311",
    "category": "test",
    "publisherId": 1,
    "authors": [
            {
                "authorId": 43
            },
            {
                "authorId": 45
            },
            {
                "authorId": 134
            },
}

person Matteoni    schedule 12.10.2020    source источник


Ответы (1)


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

var bookToCreate = _mapper.Map<Book>(bookForNewDto);

вы пытаетесь сопоставить сущность BookForNewDto с сущностью Book. Чего вам не хватает, так это того факта, что BookForNewDto имеет свойство с именем Authors, а Book имеет свойство с именем BooksAuthors. Automapper никогда не узнает, что он должен отображать Authors и BooksAuthors. Поэтому вам нужно указать эту конфигурацию с помощью 1 строки кода в вашей конфигурации Automapper:

CreateMap<BookForNewDto, Book>() (this line is already present)
                    .ForMember(dto=>dto.BooksAuthors, opt => opt.MapFrom(x => x.Authors));

Теперь, если вы отлаживаете свой код, вы увидите, что ICollection BooksAuthors был сопоставлен с вашим BookForNewDto.

Следующее сопоставление, которое вам нужно добавить, это также AuthorForNewBookDto и BookAuthor, поскольку они присутствуют в ваших двух объектах: помните, что если вы не укажете его, automapper установит нуль для ваших значений.

Вот окончательная конфигурация автомаппера:

CreateMap<BookForNewDto, Book>()
                .ForMember(dto=>dto.BooksAuthors, opt => opt.MapFrom(x => x.Authors));
CreateMap<Book, BookForNewDto>()
                .ForMember(dto => dto.PublisherId, opt => opt.MapFrom(x => x.PublisherId))
                .ForMember(dto => dto.Authors, c => c.MapFrom(c => c.BooksAuthors));

CreateMap<BookForNewDto, AuthorForNewBookDto>()
               .ForMember(dto => dto.AuthorId, opt => opt.MapFrom(x => x.Authors.Select(aaa => aaa.AuthorId)));
CreateMap<AuthorForNewBookDto, BookAuthor>();
CreateMap<BookAuthor, AuthorForNewBookDto>();
person Alessandro R    schedule 12.10.2020
comment
Спасибо за ваши усилия, Алессандро, это работает как шарм. - person Matteoni; 13.10.2020