Home > Software design >  Problem mapping DTO to Type with Many-To-Many relationships C#
Problem mapping DTO to Type with Many-To-Many relationships C#

Time:07-31

I'm trying to map a DTO to a Type. I did already did it the other way around.

The tricky part is getting the Many-to-Many relationship right.

Where Book has an ICollection<BookCategory> the BookDTO has an ICollection<int>.

namespace Core.Entities
{
    public class Book : IAggregateRoot
    {
        public int BookId { get; set; }
        public string Title { get; set; }
        public string Description { get; set; }
        public string CoverImageUrl { get; set; }
        public string Layout { get; set; }
        public ICollection<Chapter> Chapters { get; set; }
        public ICollection<BookCategory> BookCategories { get; set; }
        public ICollection<BookTag> BookTags { get; set; }
    }
}

namespace API.DTOs
{
    public class BookDTO : DTO
    {
        public int BookId { get; set; }
        public string Title { get; set; }
        public string Description { get; set; }
        public string CoverImageUrl { get; set; }
        public string Layout { get; set; }
        public ICollection<int> Categories { get; set; }
        public ICollection<int> Tags { get; set; }
    }
}

namespace Core.Entities
{
    public class BookCategory : IAggregateRoot
    {
        public int BookId { get; set; }
        public Book Book { get; set; }
        public int CategoryId { get; set; }
        public Category Category { get; set; }
    }
}

In my MappingProfile I have this code:

namespace API
{
    public class MappingProfile : Profile
    {
        public MappingProfile()
        {
            // Entity -> DTO
            // src being the concrete type T in CreateMap<T, DTO>()
            CreateMap<Book, BookDTO>()
                .ForMember(dto => dto.Categories, options => options.MapFrom(src => src.BookCategories.Select(bc => bc.CategoryId).ToList()))
                .ForMember(dto => dto.Tags, options => options.MapFrom(src => src.BookTags.Select(bt => bt.TagId).ToList()));
            ...

            // DTO -> Enitity
            CreateMap<BookDTO, Book>()
                .ForMember(
                    book => book.BookCategories,
                    options => options.ConvertUsing(dto => dto.Categories.Select(category => new BookCategory
                    {
                        BookId = dto.BookId,
                        CategoryId = category
                    }).ToList()));
            ...

            // The simplified version
            CreateMap<List<int>, List<BookCategory>>().ConstructUsing(listOfInts => listOfInts.Select(singleInt => new BookCategory {
                BookId = singleInt,
                CategoryId = singleInt
            }).ToList());
        }

        // An external conversion function
        // Usage:
        // ConvertUsing(dto => ConvertBookDtoToList(dto))
        private ICollection<BookCategory> ConvertBookDtoToList(BookDTO dto)
        {
            return dto.Categories.Select(category => new BookCategory { 
                BookId = dto.BookId,
                CategoryId = category
            }).ToList();
        }
    }
}

However using any of these approaches results in an error:

Severity    Code    Description Project File    Line    Suppression State
Error   CS0411  The type arguments for method 'IMemberConfigurationExpression<BookDTO, Book, object>.ConvertUsing<TValueConverter, TSourceMember>(Expression<Func<BookDTO, TSourceMember>>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.   API C:\Users\pebes\source\repos\ip6_uiux-ereader-app\book_designer\API\MappingProfile.cs    40  Active

I don't really understand the error message, as would appear to me to make sense.

How can I resolve this?

CodePudding user response:

CovertUsing is using for another purpose and it must be after CreateMap. It`s using to set up another way to convert one value to another

Instead of ConvertUsing you must use MapFrom, which do what you want:

CreateMap<BookDTO, Book>()
     .ForMember(
         book => book.BookCategories,
         options => options.MapFrom(dto => 
            dto.Categories.Select(category => new BookCategory
            {
               BookId = dto.BookId,
               CategoryId = category
            }).ToList()
         )
    ); 
  • Related