如何在控制器调用PaginatedItemsDto时获取对应子类的Items属性
问题:AutoMapper映射分页子类时Items属性丢失,类型鉴别器能否解决该问题?
后端定义了父类PaginatedItems及两个子类GroupedPaginatedItems、FlatPaginatedItems,各自拥有不同类型的Items属性;前端对应定义了PaginatedItemsDto及子类。控制器调用GetItemsAsync能获取包含正确Items、Skip、Take的子类数据,但通过_mapper.Map<PaginatedItemsDto>(result)映射后,Items属性丢失,仅保留Skip和Take字段。询问如何正确获取对应子类的Items属性,以及typeDiscriminator是否能解决该问题。
相关代码
DataProvider类
returnData = new GroupedPaginatedItems { Items = result, Skip = 0, Take = result.Count() };
Controller类
public async Task<PaginatedItemsDto> GetItems() { // 获取到包含正确Items、Skip、Take的子类数据 result = await _itemService.GetItemsAsync(); // 映射后Items属性丢失,仅保留Skip和Take return _mapper.Map<PaginatedItemsDto>(result); }
后端类定义
public class PaginatedItems { public int Skip { get; set; } public int Take { get; set; } } public class GroupedPaginatedItems : PaginatedItems { public Dictionary<string, Dictionary<string, List<Item>>>? Items { get; set; } } public class FlatPaginatedItems : PaginatedItems { public IEnumerable<Item>? Items { get; set; } }
前端Dto定义
// 使用如下typeDiscriminator是否有助于调用对应子类? [JsonDerivedType(typeof(GroupedPaginatedItemsDto), typeDiscriminator: "GroupedItem")] public class PaginatedItemsDto { public int Skip { get; set; } public int Take { get; set; } } public class FlatPaginatedItemsDto : PaginatedItemsDto { [JsonInclude] public List<DetailsDto> Items { get; set; } = new(); } public class GroupedPaginatedItemsDto : PaginatedItemsDto { [JsonInclude] public Dictionary<string, Dictionary<string, List<DetailsDto>>>? Items { get; set; } }
解决方案
1. 核心原因:AutoMapper默认仅映射父类属性
AutoMapper在映射时,默认只会识别目标类型(此处为PaginatedItemsDto)定义的属性,不会自动处理多态场景下的子类属性。直接映射到父类Dto会丢失子类特有的Items字段。
2. 配置AutoMapper多态映射
需要在AutoMapper的Profile中显式配置子类之间的映射,并添加条件判断以根据源类型选择正确的目标Dto:
public class MappingProfile : Profile { public MappingProfile() { // 基础父类映射 CreateMap<PaginatedItems, PaginatedItemsDto>(); // Grouped子类映射,继承父类映射规则 CreateMap<GroupedPaginatedItems, GroupedPaginatedItemsDto>() .IncludeBase<PaginatedItems, PaginatedItemsDto>(); // Flat子类映射,继承父类映射规则 CreateMap<FlatPaginatedItems, FlatPaginatedItemsDto>() .IncludeBase<PaginatedItems, PaginatedItemsDto>(); // 多态映射规则:根据源类型匹配对应Dto子类 CreateMap<PaginatedItems, PaginatedItemsDto>() .Include<GroupedPaginatedItems, GroupedPaginatedItemsDto>() .Include<FlatPaginatedItems, FlatPaginatedItemsDto>(); } }
3. 修正控制器映射与返回逻辑
控制器需要根据源实例的实际类型,映射到对应的Dto子类,而非直接映射到父类Dto:
public async Task<PaginatedItemsDto> GetItems() { var result = await _itemService.GetItemsAsync(); return result switch { GroupedPaginatedItems grouped => _mapper.Map<GroupedPaginatedItemsDto>(grouped), FlatPaginatedItems flat => _mapper.Map<FlatPaginatedItemsDto>(flat), _ => _mapper.Map<PaginatedItemsDto>(result) }; }
4. 类型鉴别器(typeDiscriminator)的作用
[JsonDerivedType]特性主要作用于JSON序列化/反序列化阶段:
- 当控制器返回子类Dto时,序列化器会自动添加类型鉴别字段(如自定义的
GroupedItem标识),前端可通过该字段识别并反序列化为对应Dto子类。 - 它不直接解决AutoMapper的映射丢失问题,但结合上述AutoMapper配置,可完整解决从后端实体到前端Dto的映射+序列化全流程问题。
补充:启用JSON多态序列化支持
若使用System.Text.Json,需手动启用多态序列化(默认关闭):
builder.Services.AddControllers() .AddJsonOptions(options => { options.JsonSerializerOptions.TypeInfoResolverChain.Insert(0, new DefaultJsonTypeInfoResolver { Modifiers = { typeInfo => { if (typeInfo.Type == typeof(PaginatedItemsDto)) { typeInfo.PolymorphismOptions = new JsonPolymorphismOptions { TypeDiscriminatorPropertyName = "itemType", // 自定义鉴别字段名 IgnoreUnrecognizedTypeDiscriminators = true }; } }} }); });
内容的提问来源于stack exchange,提问作者So25
相关产品推荐
相关产品推荐

