为何AutoMapper的ProjectTo不调用自定义方法,Map却正常?
为什么AutoMapper的
ProjectTo不调用自定义方法,但Map可以? 核心原因是两者的执行机制完全不同:
1. ProjectTo的执行逻辑
ProjectTo是基于LINQ to Entities的查询转换,它会把你的映射配置转换成对应的SQL查询语句,直接在数据库层面完成数据筛选、投影操作。
EF Core只能解析特定的表达式树(比如标准LINQ方法、实体属性访问等),你定义的AddShiftCondition是自定义静态方法,EF Core无法把这个方法的逻辑翻译成对应的SQL语句,因此这个方法不会被执行,AutoMapper会直接跳过这个无法转换的映射规则,改用默认的集合映射逻辑。
另外注意:用ProjectTo时不需要写Include,AutoMapper会根据映射关系自动生成必要的Join语句,手动加Include反而可能带来额外的性能开销。
2. Map的执行逻辑
Map是内存中的对象转换:你先通过EF Core把实体数据从数据库加载到内存(比如FirstOrDefault()已经完成了数据库查询并把数据拉到内存),之后AutoMapper在内存中对这些对象进行映射,此时自定义方法就是普通的内存操作,自然可以正常调用执行。
解决方案:让过滤逻辑支持EF Core解析
要让ProjectTo能处理你的过滤需求,需要把逻辑改成EF Core能解析的表达式,而不是自定义方法:
方法1:直接在映射中写表达式并传递参数
var configuration = new MapperConfiguration(cfg => { cfg.CreateProjection<MachineSchedule, MachineScheduleDataDto>() .ForMember(m => m.ShiftsDto, opts => opts.MapFrom((src, _, __, context) => { var targetShiftType = (EShiftType?)context.Items["ShiftType"]; return targetShiftType.HasValue ? src.Shifts.Where(s => s.Type == targetShiftType.Value) : src.Shifts; })) .ForMember(m => m.ScheduledStopsDto, opts => opts.MapFrom(m => m.ScheduledStops)) .ForMember(m => m.MachineOperationsDto, opts => opts.MapFrom(m => m.MachineOperations)); // 其他投影配置... }); // 使用时传入shiftType参数 var test = queryMapper.ProjectTo<MachineScheduleDataDto>( _typedContext!.AsNoTracking().OrderBy(m => m.MachineScheduleId), opt => opt.Items["ShiftType"] = shiftType.Value );
方法2:封装可解析的表达式树(适合复杂逻辑)
如果过滤逻辑比较复杂,可以封装成表达式树供EF Core解析:
// 定义返回表达式树的方法 private static Expression<Func<ICollection<Shift>, EShiftType?, IEnumerable<Shift>>> GetShiftFilterExpr() { return (shifts, targetType) => targetType.HasValue ? shifts.Where(s => s.Type == targetType.Value) : shifts; } // 配置投影 var configuration = new MapperConfiguration(cfg => { var shiftFilter = GetShiftFilterExpr(); cfg.CreateProjection<MachineSchedule, MachineScheduleDataDto>() .ForMember(m => m.ShiftsDto, opts => opts.MapFrom((src, _, __, context) => shiftFilter.Compile()(src.Shifts, (EShiftType?)context.Items["ShiftType"]))) // 其他映射... }); // 使用方式和方法1一致 var test = queryMapper.ProjectTo<MachineScheduleDataDto>( _typedContext!.AsNoTracking().OrderBy(m => m.MachineScheduleId), opt => opt.Items["ShiftType"] = shiftType.Value );
内容的提问来源于stack exchange,提问作者guirms
相关产品推荐
相关产品推荐

