Search code examples
c#mappingautomappingmapster

Mapster not mapping members of generic class inherited from generic list


I'm having difficulties getting all members mapped in the following scenario:

I have a class that inherits from List<T>:

public class PagedList<T> : List<T>
{
    public int CurrentPage { get; private set; }
    public int TotalPages { get; private set; }
    public int PageSize { get; private set; }
    public int TotalCount { get; private set; }
    public bool HasPrevious => CurrentPage > 1;
    public bool HasNext => CurrentPage < TotalPages;
    
    public PagedList(List<T> items, int count, int pageNumber, int pageSize)
    {
        TotalCount = count;
        PageSize = pageSize;
        CurrentPage = pageNumber;
        TotalPages = (int)Math.Ceiling(count / (double)pageSize);
        AddRange(items);
    }

    public PagedList()
    {
       //default constructor added because Mapster complained about missing default constructor
    }

    public static async Task<PagedList<T>> ToPagedListAsync(IQueryable<T> source, int pageNumber, int pageSize, CancellationToken cancellationToken = default)
    {
        var count = source.Count();
        var items = await source.Skip((pageNumber - 1) * pageSize).Take(pageSize).ToListAsync(cancellationToken);
        return new PagedList<T>(items, count, pageNumber, pageSize);
    }
}

Implementation:

public async Task<PagedList<UserDTO>> GetAllAsync(UserParameters userParameters, CancellationToken cancellationToken = default)
    {
        PagedList<User> users = await _repositoryManager.UserRepository.GetAllAsync(userParameters, cancellationToken);
        //users.Count = 5
        //users.TotalPages = 10

        TypeAdapterConfig<PagedList<User>, PagedList<UserDTO>>.NewConfig()
            .IncludeMember((member, side) => member.AccessModifier == AccessModifier.Internal || 
            member.AccessModifier == AccessModifier.ProtectedInternal);

        PagedList<UserDTO> usersDTO = users.Adapt<PagedList<UserDTO>>();
        //usersDTO.Count = 5
        //BUT usersDTO.TotalPages = 0 (should be 10)

        return usersDTO;
    }

In the above scenario, the items of the inherited List<User> list in PagedList<User> are converted properly, whereas the other members, i.e. CurrentPage, TotalPages, PageSize, TotalCount, HasPrevious and HasNext are not.

I tried to configure Mapster to include also hidden members, but to no avail:

TypeAdapterConfig<PagedList<User>, PagedList<UserDTO>>.NewConfig()
            .IncludeMember((member, side) => member.AccessModifier == AccessModifier.Internal || 
            member.AccessModifier == AccessModifier.ProtectedInternal);

How do I go about to make this conversion work?


Solution

  • I have finally found a “solution” to this problem by changing my approach.

    I changed my service implementation to:

    public async Task<(object, List<UserDTO>)> GetAllAsync(UserParameters userParameters, CancellationToken cancellationToken = default)
        {
            PagedList<User> users = await _repositoryManager.UserRepository.GetAllAsync(userParameters, cancellationToken);
    
            var metadata = new
            {
                users.TotalCount,
                users.PageSize,
                users.CurrentPage,
                users.TotalPages,
                users.HasNext,
                users.HasPrevious
            };
    
            var usersDTO = users.Adapt<List<UserDTO>>();
    
            var retVal = (metadata, usersDTO);
    
            return retVal;
        }
    

    Then in the receiving controller method, I serialise metadata and adds it to the header and returning the list.

    Like this:

    public async Task<IActionResult> GetUsers([FromQuery] UserParameters userParameters, CancellationToken cancellationToken)
        {
            var tuple = await _serviceManager.UserService.GetAllAsync(userParameters, cancellationToken);
    
            var metadata = tuple.Item1;
            var usersDTO = tuple.Item2;
    
            Response.Headers.Add("X-Pagination", JsonSerializer.Serialize(metadata));
    
            var links = _userLinks.TryGenerateLinks(usersDTO, userParameters.Fields, HttpContext);
    
            return links.HasLinks ? Ok(links.LinkedEntities) : Ok(links.ShapedEntities);
        }
    

    Thanks to Marinko Spasojevic at Code-Maze for pointing me in the right direction.