Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How to add custom Method to ProjectTo in AutoMapper?

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:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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:

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading