16

我有一个对象图,我使用 EF CodeFirst 和 AutoMapper 从数据库加载到 DTO:-

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; }
}

我的映射看起来像: -

mapper.CreateMap<Foo, FooDto>();
mapper.CreateMap<Bar, BarDto>();

到目前为止,一切都很好。我可以从我的上下文中获取实体并很好地投射到 DTO:-

var foos = context.Foos.Project().To<FooDto>();

但是,我不能用这种方法对 IQueryable中Bars的它们进行排序。SortOrder

如果我尝试:-

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>();

我得到一个例外: -

System.InvalidOperationException: Sequence contains no elements
  at System.Linq.Enumerable.First[TSource](IEnumerable`1 source)
  at AutoMapper.MappingEngine.CreateMapExpression(Type typeIn, Type typeOut)
  ...

似乎这与https://github.com/AutoMapper/AutoMapper/issues/159有关- 尽管我已经为子集合使用了复杂类型。我猜 CreateMapExpression 不支持子集合上的 OrderBy?

如果我不使用 .Project().To() 那么我可以轻松地对子集合进行排序:-

var model = context.Foos.Select(x => new FooDto()
{
  Bars = x.Bars.OrderBy(y => y.SortOrder)
});

但是我必须在任何我想使用它的地方重复映射,这违背了使用 AutoMapper 的目的。

奇怪的是:-

1)我可以对子集合执行其他(更复杂?)操作并将它们展平到我的父 DTO 没问题:-

mapper.CreateMap<Foo, FooDto>()
  .ForMember(
    x => x.AllBarsHaveAName,
    opt => opt.MapFrom(src =>
      src.Bars.All(x => x.Name != null)));

2)我可以Mapper.Map<FooDto>(foo);在内存中很好,它会排序没有问题。

可以在 IQueryable 级别对子集合进行排序,同时仍然使用 .Project().To()?

4

2 回答 2

16

最终修改了 AutoMapper 源代码以支持这种情况。希望提议的修复将被接受,但与此同时,您可以在以下位置查看详细信息:-

https://github.com/AutoMapper/AutoMapper/pull/327

于 2013-04-22T15:55:36.730 回答
0

我想知道在映射期间对 Queryable 中的 Bars 进行排序是不是最好的地方?将排序放在地图中是否意味着它在不适当的时间排序?

例如,如果您只想知道 Bars 集合是否包含 1 项,该怎么办?调用 FooDto.Bars.Any()仍会导致数据库中的排序。

也许最好在 FooDTO 中有另一个属性(在映射中忽略),如下所示:

    public IEnumerable<BarDto> SortedBars
    {
        get
        {
            if (Bars == null)
                return null;

            return Bars.OrderBy(x => x.SortOrder).ToList();
        }
    }
于 2013-04-12T16:00:31.497 回答