简体   繁体   中英

Automapper Interface Mapping

I have PagedList implementation and I am trying to use AutoMapper in order to map entity PagedList to DTO PagedList.

Here is my interface:

public interface IPagedList<T> : IList<T>
{
    PagingInformation Paging { get; set; }
}

Here is My Class Implementation:

public class PagedList<T> : List<T>, IPagedList<T> //, IList<T>
{
    public PagingInformation Paging { get; set; }

    public PagedList()
    {
    }

    public PagedList(IEnumerable<T> collection) : base(collection)
    {
    }

    public PagedList(IEnumerable<T> collection, PagingInformation paging) : base(collection)
    {
        Paging = paging;
    }

    public PagedList(int capacity) : base(capacity)
    {
    }

    PagingInformation IPagedList<T>.Paging
    {
        get => Paging;
        set => Paging = value;
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }


}

I am using Automapper like

public async Task<DomainResult<IPagedList<PositionDto>>> GetPagedListAsync(int pageIndex = 0, int pageSize = 20)
{
    return DomainResult<IPagedList<PositionDto>>.Success(_mapper.Map<IPagedList<PositionDto>>(await _positionRepository.GetPagedListAsync(pageIndex, pageSize)));
}

Without Mapper Configuration: I am getting following Error:

Error mapping types.

Mapping types: PagedList 1 -> IPagedList 1 WestCore.Shared.Collections.Pagination.PagedList 1[[WestCore.Domain.Entities.Position, WestCore.Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]] -> WestCore.Shared.Collections.Pagination.IPagedList 1[[WestCore.AppCore.Models.PositionDto, WestCore.AppCore, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]

When I Add CreateMap(typeof(PagedList<>), typeof(IPagedList<>)) into Mapper Pofile, I am getting following Error:

Method 'get_Item' in type 'Proxy WestCore.Shared.Collections.Pagination.IPagedList`1[[WestCore.AppCore.Models.PositionDto_WestCore.AppCore_Version=1.0.0.0_Culture=neutral_PublicKeyToken=null]]_WestCore.Shared_Version=1.0.0.0_Culture=neutral_PublicKeyToken=null' from assembly 'AutoMapper.Proxies, Version=0.0.0.0, Culture=neutral, PublicKeyToken=be96cd2c38ef1005' does not have an implementation.

When I add CreateMap(typeof(PagedList<>),typeof(IPagedList<>)).As(typeof(PagedList<>)); into Mapper Profile, I am not getting error however PagedList returns empty result set

I am not sure whether I have missing implementation in PagedList method or it is a configuration issue.

Edit:

PagingInformation added below:

    public class PagingInformation
{
    /// <summary>
    /// Gets the index start value.
    /// </summary>
    /// <value>The index start value.</value>
    public int IndexFrom { get; }

    /// <summary>
    /// Gets the page index (current).
    /// </summary>
    public int PageIndex { get; }

    /// <summary>
    /// Gets the page size.
    /// </summary>
    public int PageSize { get; }

    /// <summary>
    /// Gets the total count of the list of type <typeparamref name="TEntity"/>
    /// </summary>
    public int TotalCount { get; }

    /// <summary>
    /// Gets the total pages.
    /// </summary>
    public int TotalPages { get; }

    /// <summary>
    /// Gets the has previous page.
    /// </summary>
    /// <value>The has previous page.</value>
    public bool HasPreviousPage => PageIndex - IndexFrom > 0;

    /// <summary>
    /// Gets the has next page.
    /// </summary>
    /// <value>The has next page.</value>
    public bool HasNextPage => PageIndex - IndexFrom + 1 < TotalPages;

    public PagingInformation(int pageIndex, int pageSize, int indexFrom, int count)
    {
        if (indexFrom > pageIndex)
        {
            throw new ArgumentException($"indexFrom: {indexFrom} > pageIndex: {pageIndex}, must indexFrom <= pageIndex");
        }

        PageIndex = pageIndex;
        PageSize = pageSize;
        IndexFrom = indexFrom;
        TotalCount = count;
        TotalPages = (int) Math.Ceiling(TotalCount / (double) PageSize);

    }
}

Thank you

You can not use interface as a result of the mapping because mapper have no idea how to create this.

You can use ConstructUsing to create IPagedList. Like this:

AutoMapper.Mapper.CreateMap<DtoParent, ICoreParent>()
            .ConstructUsing(parentDto => new CoreParent())
            .ForMember(dest => dest.Other, opt => opt.MapFrom(src => AutoMapper.Mapper.Map<DtoChild, ICoreChild>(src.Other)));

EDIT:

Work this way:

class Example
{
    static void Main()
    {
        AutoMapper.Mapper.Initialize(config =>
        {
            config.CreateMap(typeof(PagedList<>), typeof(IPagedList<>))
                 .ConvertUsing(typeof(Converter<,>));

            config.CreateMap<Entity, DTO>();

        });

        var entityList = new PagedList<Entity>(new [] { new Entity(), }, new PagingInformation() { Total =  2, PageNumber =  1, PageSize = 10});

        var mapped = Mapper.Map<IPagedList<DTO>>(entityList);
    }
}

class Converter<TSource, TDest> : ITypeConverter<IPagedList<TSource> , IPagedList<TDest>>
{
    public IPagedList<TDest> Convert(IPagedList<TSource> source, IPagedList<TDest> destination, ResolutionContext context) =>  new PagedList<TDest>(context.Mapper.Map<IEnumerable<TDest>>(source.AsEnumerable()), source.Paging);
}

class Entity
{
    public Guid Id { get; set; } = Guid.NewGuid();
}

class DTO
{
    public Guid Id { get; set; } = Guid.NewGuid();
}

public interface IPagedList<T> : IList<T>
{
    PagingInformation Paging { get; set; }
}

public class PagingInformation
{
    public int Total { get; set; }

    public int PageSize { get; set; }

    public int PageNumber { get; set; }
}

public class PagedList<T> : List<T>, IPagedList<T>
{
    public PagingInformation Paging { get; set; }

    public PagedList() { }
    public PagedList(IEnumerable<T> collection) : base(collection) { }

    public PagedList(IEnumerable<T> collection, PagingInformation paging) : base(collection) { Paging = paging; }
}

Also probably this require to map PagingInformation in some other way as in my example both paged lists are referencing to one PagingInformation object after map, that I think is ok until PagingInformation is immutable.

Assuming you know the implementation you want to use you can at time of mapping provide the type like so

var mapper = new AutoMapper.MapperConfiguration(m => m.CreateMap<DtoParent, ICoreParent>()).CreateMapper();
var result = mapper.Map(new DtoParent { Name = "Billy Bob" }, new CoreParent());

This approach I find especially flexible when I have multiple implementations and there is not a good way to set this up in the mapping setup.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM