I have an object graph that I'm loading from a database using EF CodeFirst and AutoMapper into DTOs:-
public class Foo
{
public int Id { get; set; }
public virtual ICollection<Bar> Bars { get; set; }
}
public class Bar
{
public int Id { get; set; }
public int FooId { get; set; }
public virtual Foo Foo { get; set; }
public string Name { get; set; }
public int SortOrder { get; set; }
}
public class FooDto
{
public IEnumerable<BarDto> Bars { get; set; }
}
public class BarDto
{
public string Name { get; set; }
public int SortOrder { get; set; }
}
My mappings look like:-
mapper.CreateMap<Foo, FooDto>();
mapper.CreateMap<Bar, BarDto>();
So far, so good. I can grab the entities from my context and project to the DTO nicely:-
var foos = context.Foos.Project().To<FooDto>();
What I can't do with this approach, however, is sort the Bars
by their SortOrder
inside the IQueryable.
If I try:-
mapper.CreateMap<Foo, FooDto>()
.ForMember(
x => x.Bars
opt => opt.MapFrom(src => src.Bars.OrderBy(x => x.SortOrder)));
mapper.CreateMap<Bar, BarDto>();
var foos = context.Foos.Project().To<FooDto>();
I get an exception:-
System.InvalidOperationException: Sequence contains no elements
at System.Linq.Enumerable.First[TSource](IEnumerable`1 source)
at AutoMapper.MappingEngine.CreateMapExpression(Type typeIn, Type typeOut)
...
Seems this is related to https://github.com/AutoMapper/AutoMapper/issues/159 - though I'm already using a complex type for the child collection. I guess CreateMapExpression doesn't support OrderBy on child collections?
If I'm not using .Project().To() then I can sort the child collection easily:-
var model = context.Foos.Select(x => new FooDto()
{
Bars = x.Bars.OrderBy(y => y.SortOrder)
});
but then I have to repeat the mapping wherever I want to use it, defeating the purpose of using AutoMapper.
Curiously:-
1) I can perform other (more complicated?) operations on the child collection and flatten those into my parent DTO no problem:-
mapper.CreateMap<Foo, FooDto>()
.ForMember(
x => x.AllBarsHaveAName,
opt => opt.MapFrom(src =>
src.Bars.All(x => x.Name != null)));
2) I can Mapper.Map<FooDto>(foo);
in memory just fine, and it'll sort the bars no problem.
It's possible to sort the child collection at the IQueryable level while still using .Project().To()?
Ended up modifying the AutoMapper source code to support this scenario. Hopefully the proposed fix will be accepted, but in the meantime you can see the details at:-