I’m using .NET Core 7 and AutoMapper 12 and I’m trying to create a map from an Entity Framework query to a list of DTO’s. As I need a list of DTO’s, i’m using the .ProjectTo() function from AutoMapper
var configuration = new MapperConfiguration(cfg =>
{
cfg.CreateProjection<MachineSchedule, MachineScheduleDataDto>()
.ForMember(m => m.ShiftsDto, opts => opts.MapFrom(m => AddShiftCondition(m.Shifts, shiftType.Value))) // HERE
.ForMember(m => m.ScheduledStopsDto, opts => opts.MapFrom(m => m.ScheduledStops))
.ForMember(m => m.MachineOperationsDto, opts => opts.MapFrom(m => m.MachineOperations));
cfg.CreateProjection<MachineOperation, MachineOperationDto>()
.ForMember(m => m.EggQuantitiesDto, opts => opts.MapFrom(m => m.EggQuantities));
cfg.CreateProjection<EggQuantity, EggQuantityDto>();
cfg.CreateProjection<Shift, ShiftDto>();
cfg.CreateProjection<ScheduledStop, ScheduledStopDto>();
});
var queryMapper = configuration.CreateMapper();
var diffDays = GetWeekdaysBetweenDates(startDate, endDate);
var test = queryMapper.ProjectTo<MachineScheduleDataDto>(_typedContext?
.AsNoTracking()
.Include(m => m.Shifts)
.Include(m => m.ScheduledStops)
.Include(m => m.MachineOperations)!
.ThenInclude(m => m.EggQuantities)
.OrderBy(m => m.MachineScheduleId));
As you can see, I have the method AddShiftCondition:
private static IEnumerable<Shift>? AddShiftCondition(ICollection<Shift>? shifts, EShiftType? shiftType)
{
if (shifts != null && !shifts.Any())
return null;
if (shiftType.HasValue)
return shifts!.Where(s => s.Type == shiftType.Value);
return shifts;
}
I don’t know why, but if I use .ProjectTo(), the AddShiftCondition is not called, but if I use .Map() instead, all works fine:
var configuration = new MapperConfiguration(cfg =>
{
cfg.CreateMap<MachineSchedule, MachineScheduleDataDto>()
.ForMember(m => m.ShiftsDto, opts => opts.MapFrom(m => AddShiftCondition(m.Shifts, shiftType.Value)))
.ForMember(m => m.ScheduledStopsDto, opts => opts.MapFrom(m => m.ScheduledStops))
.ForMember(m => m.MachineOperationsDto, opts => opts.MapFrom(m => m.MachineOperations));
cfg.CreateMap<MachineOperation, MachineOperationDto>()
.ForMember(m => m.EggQuantitiesDto, opts => opts.MapFrom(m => m.EggQuantities));
cfg.CreateMap<EggQuantity, EggQuantityDto>();
cfg.CreateMap<Shift, ShiftDto>();
cfg.CreateMap<ScheduledStop, ScheduledStopDto>();
});
var queryMapper = configuration.CreateMapper();
var diffDays = GetWeekdaysBetweenDates(startDate, endDate);
var test = queryMapper.Map<MachineScheduleDataDto>(_typedContext?
.AsNoTracking()
.Include(m => m.Shifts)
.Include(m => m.ScheduledStops)
.Include(m => m.MachineOperations)!
.ThenInclude(m => m.EggQuantities)
.FirstOrDefault());
Why does it happen?
>Solution :
The easiest way would be to just scrap the method and write the expression directly:
cfg.CreateProjection<MachineSchedule, MachineScheduleDataDto>()
.ForMember(m => m.ShiftsDto,
opts => opts.MapFrom(m => m.Shifts.Where(s => s.Type == shiftType.Value))) // add empty collection handling if needed, though I would keep it simple
ProjectTo works with IQueryable and for ORM (like EF Core) it will result in translation of the LINQ query into actual SQL query and it is impossible to do (in general case) for some arbitrary method. So AutoMapper seems to ignore the call (though I would expect translation failure).
Read more:
- What is the difference between IQueryable<T> and IEnumerable<T>?
- AutoMapper ConvertUsing is not called
- AutoMapper: What is the difference between CreateMap and CreateProjection?
- AutoMapper bringing data out of nowhere after Entity Framework Query – worth noting that
Include‘s will be ignored.