Clean架构下PagedList分页后AutoMapper类型映射报错求助
问题背景
基于Clean架构开发项目,此前返回IEnumerable<T>类型时AutoMapper映射完全正常,但实现包含分页数据的PagedList<T>并添加分页头后,出现AutoMapper类型映射错误。
项目相关代码
1. 基础设施层泛型Repository
public IQueryable<T> Get() { return _context.Set<T>().AsNoTracking(); } public async Task<PagedList<T>> GetAsync(PagingParameters parameters, Expression<Func<T, object>> orderByExpression) { var query = Get(); if (orderByExpression != null) { query = query.OrderBy(orderByExpression); } var count = await query.CountAsync(); var items = await query .Skip((parameters.PageNumber - 1) * parameters.PageSize) .Take(parameters.PageSize).ToListAsync(); return new PagedList<T>(items, count, parameters.PageNumber, parameters.PageSize); }
CategoryRepository继承该泛型Repository,无自定义方法。
2. 应用层CategoryService代码
public async Task<PagedList<CategoryDTO>> GetCategories(PagingParameters parameters) { Expression<Func<Category, Object>> orderByExpression; switch (parameters.OrderedBy.ToLower()) { case "name": orderByExpression = x => x.Nome; break; default: orderByExpression = x => x.Id; break; } var categoriesEntity = await _categoryRepository.GetAsync(parameters, orderByExpression); // 报错发生在此处: return _mapper.Map<PagedList<CategoryDTO>>(categoriesEntity); }
3. CategoriesController的Get方法
[HttpGet] public async Task<ActionResult<PagedList<CategoriaDTO>>> Get([FromQuery] PagingParameters parameters) { var categorias = await _categoriaService.GetCategorias(parameters); // 分页头 var metadata = new { categorias.TotalCount, categorias.PageSize, categorias.CurrentPage, categorias.TotalPages, categorias.HasNext, categorias.HasPrevious }; Response.Headers.Add("X-Pagination", JsonSerializer.Serialize(metadata)); return Ok(categorias); }
4. PagedList定义
namespace Store.Domain.Pagination { public class PagedList<T> : List<T> { public int CurrentPage { get; set; } public int TotalPages { get; set; } public int PageSize { get; set; } public int TotalCount { get; 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); } } }
已尝试的配置与错误信息
已配置基础映射:
CreateMap<Category, CategoryDTO>().ReverseMap();
尝试过的PagedList映射配置:
CreateMap(typeof(PagedList<Category>), typeof(PagedList<CategoryDTO>));- 错误写法:
CreateMap<typeof(PagedList<Category>), typeof(PagedList<CategoryDTO>)>();
均触发错误:
"Incorrect number of arguments supplied for call to method 'Store.Application.DTOs.CategoryDTO get_Item(Int32)' (Parameter 'property')"
尝试创建PagedListDTO<T>并配置CreateMap(typeof(PagedList<Category>), typeof(PagedListDTO<CategoryDTO>));,提示PagedList需要无参构造函数,添加后错误仍存在。
可行解决方案
方案1:手动构造目标PagedList对象
绕过AutoMapper对整个PagedList的映射,手动将实体列表映射为DTO列表,再传入PagedList构造函数:
public async Task<PagedList<CategoryDTO>> GetCategories(PagingParameters parameters) { // ... 原有逻辑获取categoriesEntity ... var categoryDtos = _mapper.Map<List<CategoryDTO>>(categoriesEntity); return new PagedList<CategoryDTO>(categoryDtos, categoriesEntity.TotalCount, categoriesEntity.CurrentPage, categoriesEntity.PageSize); }
这个方法简单直接,不需要额外配置AutoMapper,避免复杂的泛型映射问题。
方案2:为AutoMapper配置自定义类型转换器
创建针对PagedList<TSource>到PagedList<TDestination>的自定义转换器,让AutoMapper知道如何处理这种泛型类型的映射:
public class PagedListConverter<TSource, TDestination> : ITypeConverter<PagedList<TSource>, PagedList<TDestination>> { private readonly IMapper _mapper; public PagedListConverter(IMapper mapper) { _mapper = mapper; } public PagedList<TDestination> Convert(PagedList<TSource> source, PagedList<TDestination> destination, ResolutionContext context) { var destinationItems = _mapper.Map<List<TDestination>>(source); return new PagedList<TDestination>(destinationItems, source.TotalCount, source.CurrentPage, source.PageSize); } }
然后在AutoMapper配置中注册转换器:
CreateMap(typeof(PagedList<>), typeof(PagedList<>)) .ConvertUsing(typeof(PagedListConverter<,>));
同时确保已经配置了TSource到TDestination的基础映射(比如Category到CategoryDTO)。
方案3:修改PagedList结构,避免继承List
原PagedList<T>继承自List<T>,AutoMapper在处理继承自集合的类型时容易出现冲突。可以修改PagedList,将内部集合作为属性而非继承:
public class PagedList<T> { public List<T> Items { get; set; } public int CurrentPage { get; set; } public int TotalPages { get; set; } public int PageSize { get; set; } public int TotalCount { get; set; } public bool HasPrevious => CurrentPage > 1; public bool HasNext => CurrentPage < TotalPages; public PagedList(List<T> items, int count, int pageNumber, int pageSize) { Items = items; TotalCount = count; PageSize = pageSize; CurrentPage = pageNumber; TotalPages = (int)Math.Ceiling(count / (double)pageSize); } }
之后配置AutoMapper映射时,只需要配置基础的Category到CategoryDTO,以及PagedList<Category>到PagedList<CategoryDTO>,AutoMapper会自动映射Items属性和其他分页属性。
内容的提问来源于stack exchange,提问作者Nayala

