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

#c# #.net-core #many-to-many #automapper #automapping

#c# #.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; }
    }
  

BookController (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 // Я прикрепил только раздел, касающийся добавления новой книги.

 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 для получения результата JSON ниже? У меня проблема с авторами. Я все еще получаю пустой список.

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

Ответ №1:

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

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

вы пытаетесь сопоставить объект BookForNewDto с объектом Book one. Чего вам не хватает, так это того факта, что 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, поскольку они присутствуют в ваших 2 сущностях: помните, что если вы не укажете его, automapper установит значение null для ваших значений.

Вот окончательная конфигурация 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>();