.NET中如何用.Select()映射含不同类型集合属性的对象
在.NET 7 Entity Framework中映射ICollection类型的DTO属性
你需要将EF查询中的MachineOperation实体集合映射到MachineScheduleDataDto的ICollection<MachineOperationDto>属性,以下是两种可行的解决方案:
方案1:手动投影(无需第三方库)
直接在EF的Select方法中对集合属性逐个转换为DTO类型,这是最直接的方式:
修改你的查询代码如下:
var test = _typedContext .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) .OrderBy(m => m.MachineScheduleId) .Select(m => new MachineScheduleDataDto { MachineScheduleId = m.MachineScheduleId, // 对MachineOperations集合逐个映射为MachineOperationDto MachineOperationsDto = m.MachineOperations.Select(mo => new MachineOperationDto { MachineOperationId = mo.MachineOperationId, MachineStatus = mo.MachineStatus, // 注意:直接映射完整的MachineScheduleDataDto会触发循环引用 // 这里建议只保留关联ID,或者按需映射必要字段 MachineScheduleDto = new MachineScheduleDataDto { MachineScheduleId = m.MachineScheduleId, MachineOperationsDto = new List<MachineOperationDto>() // 避免循环,可留空 } }).ToList() }) .ToList();
关键提示
你的MachineOperationDto中包含MachineScheduleDataDto属性,这会导致循环引用(父DTO包含子DTO,子DTO又反向包含父DTO),序列化时必然抛出异常。建议优化DTO设计,将MachineScheduleDto替换为仅存关联ID:
public record MachineOperationDto { public int MachineOperationId { get; set; } public EMachineStatus MachineStatus { get; set; } public int MachineScheduleId { get; set; } // 替换原有的MachineScheduleDto }
这种设计既避免循环问题,也能满足大多数业务场景对关联标识的需求。
方案2:用AutoMapper简化映射
如果项目中频繁需要实体与DTO的映射,使用AutoMapper可以大幅简化代码:
步骤1:配置映射规则
创建AutoMapper配置类:
public class MachineMappingProfile : Profile { public MachineMappingProfile() { // 映射MachineSchedule到MachineScheduleDataDto CreateMap<MachineSchedule, MachineScheduleDataDto>(); // 映射MachineOperation到MachineOperationDto CreateMap<MachineOperation, MachineOperationDto>() // 推荐:只映射关联ID避免循环 .ForMember(dest => dest.MachineScheduleId, opt => opt.MapFrom(src => src.MachineSchedule.MachineScheduleId)); // 若坚持保留MachineScheduleDto,需手动避免循环: // .ForMember(dest => dest.MachineScheduleDto, opt => opt.MapFrom(src => new MachineScheduleDataDto { MachineScheduleId = src.MachineSchedule.MachineScheduleId })); } }
步骤2:在查询中使用投影
通过AutoMapper的ProjectTo方法直接将EF查询结果转换为DTO:
// 先在构造函数中注入IMapper实例 private readonly IMapper _mapper; private readonly YourDbContext _typedContext; public YourService(IMapper mapper, YourDbContext typedContext) { _mapper = mapper; _typedContext = typedContext; } // 编写查询 var test = _typedContext .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) .OrderBy(m => m.MachineScheduleId) // 直接投影为目标DTO .ProjectTo<MachineScheduleDataDto>(_mapper.ConfigurationProvider) .ToList();
内容的提问来源于stack exchange,提问作者guirms
相关产品推荐
相关产品推荐

