Automapper包含抽象泛型父类时引发“未派生自”错误
解决AutoMapper泛型继承映射的冗余问题
这个问题我之前也碰到过,核心原因是C#泛型的不变性导致的:哪怕QuestionB继承自BaseQuestion,BaseAnswer<QuestionB>也不会自动被视为BaseAnswer<BaseQuestion>的子类,所以你用IncludeBase的时候才会抛出类型不匹配的错误。
下面给你几个简洁的解决方法,不用重复编写每个泛型类的映射:
方法一:开放泛型映射(推荐)
AutoMapper支持开放泛型的映射配置,能一次性处理所有BaseAnswer<T>(T继承自BaseQuestion)到AnswerDTO的映射逻辑,不用逐个写具体的泛型类配置。
步骤1:添加非泛型接口(可选但推荐)
为了避免使用dynamic,给BaseAnswer<T>定义一个非泛型接口,暴露通用属性(比如User):
public interface IBaseAnswer { // 假设你的User类是这样的,根据实际情况调整 User User { get; set; } } // 让泛型基类实现这个接口 public abstract class BaseAnswer<T> : IBaseAnswer where T : BaseQuestion { public User User { get; set; } // 其他泛型相关属性/方法 }
步骤2:配置开放泛型映射
// 配置所有BaseAnswer<T>到AnswerDTO的通用映射 CreateMap(typeof(BaseAnswer<>), typeof(AnswerDTO)) .ForMember(dto => dto.UserDisplay, o => o.MapFrom(x => $"{x.User.FirstName} {x.User.LastName}")); // 只需要给每个具体Answer类配置简单的映射,AutoMapper会自动应用上面的通用规则 CreateMap<AnswerA, AnswerDTO>(); CreateMap<AnswerB, AnswerDTO>(); CreateMap<AnswerC, AnswerDTO>();
如果不想加非泛型接口,也可以用dynamic来访问User属性,但强类型的方式更安全。
方法二:自动注册所有Answer类(进阶)
如果以后还要新增更多AnswerX类,可以用反射自动扫描并注册映射,彻底不用手动写每个CreateMap:
public class AnswerMappingProfile : Profile { public AnswerMappingProfile() { // 先配置开放泛型的通用映射逻辑 CreateMap(typeof(BaseAnswer<>), typeof(AnswerDTO)) .ForMember(dto => dto.UserDisplay, o => o.MapFrom(x => $"{((dynamic)x).User.FirstName} {((dynamic)x).User.LastName}")); // 反射找到所有继承自BaseAnswer<T>的具体Answer类 var assembly = typeof(AnswerA).Assembly; // 替换成你的程序集 var answerTypes = assembly.GetTypes() .Where(t => !t.IsAbstract && !t.IsInterface && t.BaseType != null && t.BaseType.IsGenericType && t.BaseType.GetGenericTypeDefinition() == typeof(BaseAnswer<>)); // 自动注册每个Answer类到AnswerDTO的映射 foreach (var type in answerTypes) { CreateMap(type, typeof(AnswerDTO)); } } }
这样不管以后加多少AnswerX,只要继承自BaseAnswer<T>,都会自动被注册映射,完全不用修改配置代码。
方法三:通用类型转换器
如果开放泛型不适合你的场景,可以写一个通用的转换器,复用映射逻辑:
public class BaseAnswerConverter<T> : ITypeConverter<BaseAnswer<T>, AnswerDTO> where T : BaseQuestion { public AnswerDTO Convert(BaseAnswer<T> source, AnswerDTO destination, ResolutionContext context) { if (source == null) return null; // 复用的映射逻辑 var dto = destination ?? new AnswerDTO(); dto.UserDisplay = $"{source.User.FirstName} {source.User.LastName}"; // 其他属性可以让AutoMapper自动映射,或者手动处理 context.Mapper.Map(source, dto); return dto; } } // 配置时复用转换器 CreateMap<AnswerA, AnswerDTO>().ConvertUsing(new BaseAnswerConverter<QuestionA>()); CreateMap<AnswerB, AnswerDTO>().ConvertUsing(new BaseAnswerConverter<QuestionB>()); CreateMap<AnswerC, AnswerDTO>().ConvertUsing(new BaseAnswerConverter<QuestionC>());
这个方法虽然还是要写三个CreateMap,但核心映射逻辑只写了一次,避免了冗余。
内容的提问来源于stack exchange,提问作者Piotrek
相关产品推荐
相关产品推荐

