I am having an issue using an AutoMapper (version 5.1.1) projection combined with a Linq OrderBy Child property expression. I am using Entity Framework Core (version 1.0.0). I am getting the following error:
"must be reducible node"
My DTO objects are as follows
public class OrganizationViewModel
{
public virtual int Id { get; set; }
[Display(Name = "Organization Name")]
public virtual string Name { get; set; }
public virtual bool Active { get; set; }
public virtual int OrganizationGroupId { get; set; }
public virtual string OrganizationGroupName { get; set; }
public virtual int StrategyId { get; set; }
public virtual string StrategyName { get; set; }
public virtual OrganizationGroupViewModel OrganizationGroup { get; set; }
}
public class OrganizationGroupViewModel
{
public virtual int Id { get; set; }
[Display(Name = "Organization Group Name")]
public virtual string Name { get; set; }
public virtual bool Active { get; set; }
}
My corresponding entity models are as follows:
public class Organization
{
public int Id { get; set; }
public string Name { get; set; }
public string TimeZone { get; set; }
public bool Active { get; set; }
//FKs
public int OrganizationGroupId { get; set; }
public int StrategyId { get; set; }
//Navigation
public virtual OrganizationGroup OrganizationGroup { get; set; }
public virtual Strategy Strategy { get; set; }
[JsonIgnore]
public virtual List<AppointmentReminder> AppointmentReminders { get; set; }
}
public class OrganizationGroup
{
public int Id { get; set; }
public string Name { get; set; }
public bool Active { get; set; }
public virtual List<Organization> Organizations { get; set; }
}
My AutoMapper profiles are as follows:
public class OrganizationMapperProfile : Profile
{
public OrganizationMapperProfile()
{
CreateMap<Task<Organization>, Task<OrganizationViewModel>>();
CreateMap<Organization, OrganizationViewModel>()
.ForMember(dest => dest.OrganizationGroupName, opt => opt.MapFrom(src => src.OrganizationGroup.Name));
CreateMap<OrganizationInput, Organization>()
.ForMember(x => x.Id, opt => opt.Ignore());
}
}
public class OrganizationGroupMapperProfile : Profile
{
public OrganizationGroupMapperProfile()
{
CreateMap<Task<OrganizationGroup>, Task<OrganizationGroupViewModel>>();
CreateMap<OrganizationGroup, OrganizationGroupViewModel>();
CreateMap<OrganizationGroupInput, OrganizationGroup>()
.ForMember(x => x.Id, opt => opt.Ignore());
}
}
When I run the following statements I am able to run and get results from the first 2 statements:
var tmp = await _context.Organizations.Include(x => x.OrganizationGroup).OrderBy(x => x.OrganizationGroup.Name).ToListAsync();
var tmp4 = await _context.Organizations.Include(x => x.OrganizationGroup).OrderBy("OrganizationGroup.Name").ToListAsync();
But when I add the ProjectTo I get the error listed above:
var tmp5 = await _context.Organizations.Include(x => x.OrganizationGroup).OrderBy(x => x.OrganizationGroup.Name).ProjectTo<OrganizationViewModel>().ToListAsync();
var tmp6 = await _context.Organizations.Include(x => x.OrganizationGroup).OrderBy("OrganizationGroup.Name").ProjectTo<OrganizationViewModel>().ToListAsync();
As some additional information, I am able to OrderBy with Projections working on properties of the parent class, such as:
var tmp7 = await _context.Organizations.Include(x => x.OrganizationGroup).OrderBy(x => x.Name).ProjectTo<OrganizationViewModel>().ToListAsync();
var tmp8 = await _context.Organizations.Include(x => x.OrganizationGroup).OrderBy("Name").ProjectTo<OrganizationViewModel>().ToListAsync();
Anyone run into this issue before? Looks like I'm trying to do something that is otherwise not supported, is that by design? Thanks for any help/insight.
Looks like the problem is caused by the OrganizationGroup
property of the OrganizationViewModel
class - AutoMapper generates a null check which EF Core doesn't like in the combination with your OrderBy
(I guess just one of the many bugs currently in EF Core). It can easily be reproduced by the following simple manual projection query:
var tmp5a = _context.Organizations
.OrderBy(x => x.OrganizationGroup.Name)
.Select(e => new OrganizationViewModel
{
Id = e.Id,
OrganizationGroup = e.OrganizationGroup != null ? new OrganizationGroupViewModel
{
Id = e.OrganizationGroup.Id,
Name = e.OrganizationGroup.Name,
Active = e.OrganizationGroup.Active,
} : null,
})
.ToList();
To fix the issue, configure AutoMapper to not generate null
check for that property as follows:
CreateMap<Organization, OrganizationViewModel>()
.ForMember(dest => dest.OrganizationGroup, opt => opt.AllowNull())
.ForMember(dest => dest.OrganizationGroupName, opt => opt.MapFrom(src => src.OrganizationGroup.Name));