.NET Core中AutoMapper ProjectTo未正确应用EF查询过滤条件问题
问题:EF Core查询结合AutoMapper ProjectTo后过滤条件失效
我使用.NET Core 7、Entity Framework Core 7.0.13和AutoMapper 12.0.3编写了如下EF查询:
var test = _context.Set<T> .AsNoTracking() .Include(m => m.MachineOperations! .Where(m => m.InsertedAt.Date <= endDate.Date && m.EndTime <= endDate.TimeOfDay && m.InsertedAt.Date >= startDate.Date && m.StartTime >= startDate.TimeOfDay)) .ThenInclude(m => m.EggQuantities) .Where(m => diffDays.Contains(m.WeekDay) && m.MachineOperations!.Any() && m.InitialProductionTime <= startDate.TimeOfDay && m.FinalProductionTime >= startDate.TimeOfDay); var test2 = _mapper.ProjectTo<MachineScheduleDataDto>(test);
问题:当传入本应无MachineOperation返回的参数时,原查询test结果为空,但经过ProjectTo转换后却意外出现数据。若在查询末尾添加.ToList()再转为AsQueryable()传入映射,过滤条件可正常工作,但此方式会查询所有实体属性,无法采用。
我的DTO类定义:
public record MachineScheduleDataDto { public required ICollection<MachineOperationDto> MachineOperationsDto { get; set; } } public record MachineOperationDto { public int MachineOperationId { get; set; } public EMachineStatus MachineStatus { get; set; } public virtual required MachineScheduleDataDto MachineScheduleDto { get; set; } }
映射配置:
CreateMap<MachineSchedule, MachineScheduleDataDto>() .ForMember(p => p.MachineOperationsDto, opts => opts.MapFrom(m => m.MachineOperations)); CreateMap<MachineOperation, MachineOperationDto>() .ForMember(p => p.MachineScheduleDto, opts => opts.MapFrom(m => m.MachineSchedule));
原因分析
核心问题是EF Core的Include.Where导航属性过滤逻辑,不会被AutoMapper的ProjectTo自动继承:
Include.Where是对关联实体做查询级过滤,但AutoMapper生成投影查询时,会直接读取原始的MachineOperations导航属性,忽略你在Include中添加的过滤条件。- 原查询里的
m.MachineOperations!.Any()判断的是经过Include过滤后的集合是否为空,但ProjectTo会把这个判断转换成对原始MachineOperations集合的检查,逻辑不一致,导致结果偏差。 - 另外DTO中
MachineOperationDto包含MachineScheduleDto,存在循环引用风险,也会干扰映射逻辑。
解决方案
方案1:在映射配置中显式添加过滤条件
修改映射规则,将过滤逻辑直接写入MapFrom,让ProjectTo把条件整合到最终SQL查询中:
CreateMap<MachineSchedule, MachineScheduleDataDto>() .ForMember(p => p.MachineOperationsDto, opts => opts.MapFrom((m, _, _, context) => m.MachineOperations! .Where(mo => mo.InsertedAt.Date <= ((DateTime)context.Items["EndDate"]).Date && mo.EndTime <= ((DateTime)context.Items["EndDate"]).TimeOfDay && mo.InsertedAt.Date >= ((DateTime)context.Items["StartDate"]).Date && mo.StartTime >= ((DateTime)context.Items["StartDate"]).TimeOfDay)));
调用ProjectTo时传入上下文参数:
var test2 = _mapper.ProjectTo<MachineScheduleDataDto>(test, opt => { opt.Items["StartDate"] = startDate; opt.Items["EndDate"] = endDate; });
方案2:直接在查询中做投影过滤(推荐)
放弃依赖Include,直接在查询里通过Select构建DTO结构,确保只查询需要的属性,同时避免映射逻辑冲突:
var query = _context.Set<MachineSchedule>() .AsNoTracking() .Where(m => diffDays.Contains(m.WeekDay) && m.MachineOperations!.Any(mo => mo.InsertedAt.Date <= endDate.Date && mo.EndTime <= endDate.TimeOfDay && mo.InsertedAt.Date >= startDate.Date && mo.StartTime >= startDate.TimeOfDay) && m.InitialProductionTime <= startDate.TimeOfDay && m.FinalProductionTime >= startDate.TimeOfDay) .Select(m => new MachineScheduleDataDto { MachineOperationsDto = m.MachineOperations! .Where(mo => mo.InsertedAt.Date <= endDate.Date && mo.EndTime <= endDate.TimeOfDay && mo.InsertedAt.Date >= startDate.Date && mo.StartTime >= startDate.TimeOfDay) .Select(mo => new MachineOperationDto { MachineOperationId = mo.MachineOperationId, MachineStatus = mo.MachineStatus // 移除循环引用的MachineScheduleDto,或添加Ignore配置 }).ToList() });
额外优化:处理循环引用
修改映射配置,忽略会导致循环引用的属性:
CreateMap<MachineOperation, MachineOperationDto>() .ForMember(p => p.MachineScheduleDto, opts => opts.Ignore());
内容的提问来源于stack exchange,提问作者guirms
相关产品推荐
相关产品推荐

