You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 17:58:11