如何使用AutoMapper映射泛型类型间的自定义IQueryResult接口
IQueryResult<T>属性为Null的问题 你遇到的核心问题是AutoMapper默认无法直接操作带有私有setter的属性,导致映射后的IQueryResult<DtoA>中Payload和PageInfo都为null。下面给你几个实用的解决方案,按推荐优先级排序:
方案1:利用AutoMapper构造函数映射(推荐)
你的QueryResult<T>已经提供了带参数的构造函数,AutoMapper可以自动识别并通过构造函数初始化对象,完全不需要直接操作私有属性。只需要调整泛型映射的配置,针对实现类而非接口进行映射:
// 替换原来的接口间映射,改为接口到实现类的泛型映射 CreateMap(typeof(IQueryResult<>), typeof(QueryResult<>));
这样当你执行_mapper.Map<IQueryResult<DtoA>>(entities)时,AutoMapper会自动实例化QueryResult<DtoA>,并将映射后的IEnumerable<DtoA>(原EntityA集合转换而来)和原PageInfo通过构造函数传入,完美保留封装性。
如果AutoMapper的自动构造函数解析不够灵活,也可以显式指定构造逻辑:
CreateMap(typeof(IQueryResult<>), typeof(QueryResult<>)) .ConstructUsing((source, context) => { // 获取目标泛型类型 var targetGenericType = context.DestinationType.GetGenericArguments()[0]; // 映射Payload集合 var sourcePayload = source.GetType().GetProperty("Payload").GetValue(source); var mappedPayload = context.Mapper.Map( sourcePayload, typeof(IEnumerable<>).MakeGenericType(source.GetType().GetGenericArguments()[0]), typeof(IEnumerable<>).MakeGenericType(targetGenericType) ); // 获取原PageInfo var pageInfo = source.GetType().GetProperty("PageInfo").GetValue(source); // 通过反射创建QueryResult<T>实例 return Activator.CreateInstance( typeof(QueryResult<>).MakeGenericType(targetGenericType), mappedPayload, pageInfo ); });
方案2:显式配置允许访问私有Setter
如果不想依赖构造函数,可以通过AutoMapper的配置项,明确允许框架访问私有setter来赋值:
CreateMap(typeof(IQueryResult<>), typeof(IQueryResult<>)) .ForMember("Payload", opt => opt.MapFrom(src => src.Payload)) .ForMember("PageInfo", opt => opt.MapFrom(src => src.PageInfo)) .ForAllMembers(opt => opt.SetterModifier(MemberSetterModifier.Private));
SetterModifier(MemberSetterModifier.Private)会告诉AutoMapper:即使属性的setter是私有的,也可以尝试赋值。
方案3:调整属性Setter的访问修饰符(如果代码结构允许)
如果你的项目架构允许,可以把Payload和PageInfo的setter改为protected internal,这样AutoMapper在同一程序集或友元程序集中就能直接访问,不需要额外配置:
public IEnumerable<T> Payload { get; protected internal set; } public PageInfo PageInfo { get; protected internal set; }
这种方式最简单,但需要权衡封装性和便利性。
测试验证
完成配置后,运行你的控制器代码,检查result.Payload和result.PageInfo是否已经正确赋值:
IQueryResult<EntityA> entities = service.GetAll(); IQueryResult<DtoA> result = _mapper.Map<IQueryResult<DtoA>>(entities); // 调试或打印result的属性值,确认是否正常
内容的提问来源于stack exchange,提问作者Jester

