Automapper i relacja wiele do wielu, a metoda POST

0

Cześć, od jakiegoś czasu przeglądam forum 4programmers i korzystam z jego zasobów, ale dopiero teraz postanowiłem założyć konto i poszukać pomocy w czymś, czego nie mogę znaleźć w internecie. Z góry zaznaczę, że jestem laikiem i zdaje sobie sprawę, że pewne części mojego kodu powinny wyglądać lepiej lub kompletnie inaczej.
Aktualnie uczę się tworząc proste API w którym mam różne relacje miedzy encjami, największy problem mam właśnie z tytułową relacją wiele do wielu i metodą POST.

Postaram się opisać, w czym leży mój problem:
Posiadam dwie klasy (Book i Author) i klasę łączącą (BookAuthor), które tworzą relacje wiele do wielu.
Oprócz tego mam kilka klas DTO. BookController z metodą POST i Profiler dla AutoMappera (wszystko poniżej).
Po wysłaniu zapytania POST do API mój wynik w JSON wygląda następująco:

JSON BODY
{
    "title": "sample title5",
    "authors": [
        { "authorId": 5 },
        { "authorId": 2 }
    ]
}

RESULT
{
    "title": "sample title",
    "authors": []
}

Chciałbym, aby wyglądało to tak: (tzn. zamiast pustej tablicy, wyświetlała się lista autorów)

{
    "title": "sample title",
     "authors": [
            {
                "authorId": 5,
                "authorId": 2
            },
}

**** KOD ****

    public class Book
    {
        public int BookId { get; set; }
        public string Title { 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; }
    }

DTOs

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

BookController // metoda POST

        [HttpPost]
        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

            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.Select(cs => cs.Author).ToList()));

            CreateMap<AuthorForNewBookDto, Author>()
                .ForMember(dto => dto.AuthorId, opt => opt.MapFrom(x => x.AuthorId));

Sama funkcjonalność, jeśli chodzi o dodanie książki z listą autorów do bazy działa prawidłowo tzn. dodaje mi książkę do tabeli Books i dodają mi się nowe rekordy do tabeli BooksAuthors.
Jedyne co mnie dręczy to pusta lista 'authors' w wyniku JSON.

Mam nadzieje, że dobrze opisałem, w czym mam problem, a także liczę na wasze sugestie i dobre rady. Osobiście uważam, że popełniam jakiś błąd w kontrolerze albo w profilerze.
Pozdrawiam.

1

A co to za PublisherId, który jest w profilu automappera, a nie ma go w modelach?

Nie masz mapowań przy CreateMap<BookForNewDto, Book>(); - w Book jest BookAuthors, a w BookForNewDto jest Authors, więc Automapper chyba tutaj nie wie, że ma to zmapować. Powinno być podobnie jak w przypadku tego odwrotnego mapowania w następnej linijce.

0
piotrevic napisał(a):

A co to za PublisherId, który jest w profilu automappera, a nie ma go w modelach?

Nie masz mapowań przy CreateMap<BookForNewDto, Book>(); - w Book jest BookAuthors, a w BookForNewDto jest Authors, więc Automapper chyba tutaj nie wie, że ma to zmapować. Powinno być podobnie jak w przypadku tego odwrotnego mapowania w następnej linijce.

PublisherId mam w klasie Book, nie zamieszczałem tutaj tego, aby nie dodawać zbędnych właściwości w poście.

Zmiana nazwy na taką samą jak w DTO nie pomogła, tutaj chyba brakuje jakichś mapowań, w sumie tutaj mam tak jakby zagnieżdżone te DTOs to może tym kierunku cos?

0

Udało mi się rozwiązać problem, brakowało mi kilku mapowań no i musiałem dać znać AutoMapperowi, że moje BooksAuthors to jest to samo co Authors (wskazówka @piotrevic była pomocna). Zamieszczam prawidłową konfiguracje, może komuś się przyda.

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>();

1 użytkowników online, w tym zalogowanych: 0, gości: 1