Can Automapper map a paged list?

前端 未结 6 1887
我寻月下人不归
我寻月下人不归 2021-02-01 19:38

I\'d like to map a paged list of business objects to a paged list of view model objects using something like this:

var listViewModel = _mappingEngine.Map

        
相关标签:
6条回答
  • 2021-02-01 19:58

    AutoMapper automatically handles conversions between several types of lists and arrays: http://automapper.codeplex.com/wikipage?title=Lists%20and%20Arrays

    It doesn't appear to automatically convert custom types of lists inherited from IList, but a work around could be:

        var pagedListOfRequestForQuote = new PagedList<RequestForQuoteViewModel>(
            AutoMapper.Mapper.Map<List<RequestForQuote>, List<RequestForQuoteViewModel>>(((List<RequestForQuote>)requestForQuotes),
            page ?? 1,
            pageSize
    
    0 讨论(0)
  • 2021-02-01 20:02

    I created a little wrapper around AutoMapper to map PagedList<DomainModel> to PagedList<ViewModel>.

    public class MappingService : IMappingService
    {
        public static Func<object, Type, Type, object> AutoMap = (a, b, c) =>
        {
            throw new InvalidOperationException(
                "The Mapping function must be set on the MappingService class");
        };
    
        public PagedList<TDestinationElement> MapToViewModelPagedList<TSourceElement, TDestinationElement>(PagedList<TSourceElement> model)
        {
            var mappedList = MapPagedListElements<TSourceElement, TDestinationElement>(model);
            var index = model.PagerInfo.PageIndex;
            var pageSize = model.PagerInfo.PageSize;
            var totalCount = model.PagerInfo.TotalCount;
    
            return new PagedList<TDestinationElement>(mappedList, index, pageSize, totalCount);
        }
    
        public object Map<TSource, TDestination>(TSource model)
        {
            return AutoMap(model, typeof(TSource), typeof(TDestination));
        }
    
        public object Map(object source, Type sourceType, Type destinationType)
        {
            if (source is IPagedList)
            {
                throw new NotSupportedException(
                    "Parameter source of type IPagedList is not supported. Please use MapToViewModelPagedList instead");
            }
    
            if (source is IEnumerable)
            {
                IEnumerable<object> input = ((IEnumerable)source).OfType<object>();
                Array a = Array.CreateInstance(destinationType.GetElementType(), input.Count());
    
                int index = 0;
                foreach (object data in input)
                {
                    a.SetValue(AutoMap(data, data.GetType(), destinationType.GetElementType()), index);
                    index++;
                }
                return a;
            }
    
            return AutoMap(source, sourceType, destinationType);
        }
    
        private static IEnumerable<TDestinationElement> MapPagedListElements<TSourceElement, TDestinationElement>(IEnumerable<TSourceElement> model)
        {
            return model.Select(element => AutoMap(element, typeof(TSourceElement), typeof(TDestinationElement))).OfType<TDestinationElement>();
        }
    }
    

    Usage:

    PagedList<Article> pagedlist = repository.GetPagedList(page, pageSize);
    mappingService.MapToViewModelPagedList<Article, ArticleViewModel>(pagedList);
    

    It is important that you would have to use the element types!

    If you have any question or suggestions, please feel free to comment :)

    0 讨论(0)
  • 2021-02-01 20:14

    Using jrummell's answer, I created an extension method that works with Troy Goode's PagedList. It keeps you from having to put so much code everywhere...

        public static IPagedList<TDestination> ToMappedPagedList<TSource, TDestination>(this IPagedList<TSource> list)
        {
            IEnumerable<TDestination> sourceList = Mapper.Map<IEnumerable<TSource>, IEnumerable<TDestination>>(list);
            IPagedList<TDestination> pagedResult = new StaticPagedList<TDestination>(sourceList, list.GetMetaData());
            return pagedResult;
    
        }
    

    Usage is:

    var pagedDepartments = database.Departments.OrderBy(orderBy).ToPagedList(pageNumber, pageSize).ToMappedPagedList<Department, DepartmentViewModel>();
    
    0 讨论(0)
  • 2021-02-01 20:21

    If you're using Troy Goode's PageList, there's a StaticPagedList class that can help you map.

    // get your original paged list
    IPagedList<Foo> pagedFoos = _repository.GetFoos(pageNumber, pageSize);
    // map to IEnumerable
    IEnumerable<Bar> bars = Mapper.Map<IEnumerable<Bar>>(pagedFoos);
    // create an instance of StaticPagedList with the mapped IEnumerable and original IPagedList metadata
    IPagedList<Bar> pagedBars = new StaticPagedList<Bar>(bars, pagedFoos.GetMetaData());
    
    0 讨论(0)
  • 2021-02-01 20:21

    I needed to return a serializable version of IPagedList<> with AutoMapper version 6.0.2 that supports the IMapper interface for ASP.NET Web API. So, if the question was how do I support the following:

    //Mapping from an enumerable of "foo" to a different enumerable of "bar"...
    var listViewModel = _mappingEngine.Map<IPagedList<RequestForQuote>, PagedViewModel<RequestForQuoteViewModel>>(requestForQuotes);
    

    Then one could do this:

    Define PagedViewModel<T>
    Source: AutoMapper Custom Type Converter not working

    public class PagedViewModel<T>
    {
        public int FirstItemOnPage { get; set; }
        public bool HasNextPage { get; set; }
        public bool HasPreviousPage { get; set; }
        public bool IsFirstPage { get; set; }
        public bool IsLastPage { get; set; }
        public int LastItemOnPage { get; set; }
        public int PageCount { get; set; }
        public int PageNumber { get; set; }
        public int PageSize { get; set; }
        public int TotalItemCount { get; set; }
        public IEnumerable<T> Subset { get; set; }
    }
    

    Write open generic converter from IPagedList<T> to PagedViewModel<T>
    Source: https://github.com/AutoMapper/AutoMapper/wiki/Open-Generics

    public class Converter<TSource, TDestination> : ITypeConverter<IPagedList<TSource>, PagedViewModel<TDestination>>
    {
        public PagedViewModel<TDestination> Convert(IPagedList<TSource> source, PagedViewModel<TDestination> destination, ResolutionContext context)
        {
            return new PagedViewModel<TDestination>()
            {
                FirstItemOnPage = source.FirstItemOnPage,
                HasNextPage = source.HasNextPage,
                HasPreviousPage = source.HasPreviousPage,
                IsFirstPage = source.IsFirstPage,
                IsLastPage = source.IsLastPage,
                LastItemOnPage = source.LastItemOnPage,
                PageCount = source.PageCount,
                PageNumber = source.PageNumber,
                PageSize = source.PageSize,
                TotalItemCount = source.TotalItemCount,
                Subset = context.Mapper.Map<IEnumerable<TSource>, IEnumerable<TDestination>>(source) //User mapper to go from "foo" to "bar"
            };
        }
    }
    

    Configure mapper

    new MapperConfiguration(cfg =>
        {
            cfg.CreateMap<RequestForQuote, RequestForQuoteViewModel>();//Define each object you need to map
            cfg.CreateMap(typeof(IPagedList<>), typeof(PagedViewModel<>)).ConvertUsing(typeof(Converter<,>)); //Define open generic mapping
        });
    
    0 讨论(0)
  • 2021-02-01 20:23

    AutoMapper does not support this out of the box, as it doesn't know about any implementation of IPagedList<>. You do however have a couple of options:

    1. Write a custom IObjectMapper, using the existing Array/EnumerableMappers as a guide. This is the way I would go personally.

    2. Write a custom TypeConverter, using:

      Mapper
          .CreateMap<IPagedList<Foo>, IPagedList<Bar>>()
          .ConvertUsing<MyCustomTypeConverter>();
      

      and inside use Mapper.Map to map each element of the list.

    0 讨论(0)
提交回复
热议问题