2013-02-14 12 views
14

Mam wykres obiektu, który mam załadowany z bazy danych przy użyciu EF CodeFirst i AutoMapper do DTOs: -.AutoMapper Project() do() i sortowanie kolekcji dziecięcej

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

Moi mapowania wyglądać następująco: -

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

Jak dotąd, tak dobrze. Mogę chwycić podmioty z mojego kontekstu i projektu do DTO ładnie: -

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

Co nie mogę zrobić z tym podejściem jest jednak posortować Bars przez ich SortOrder wewnątrz IQueryable.

Gdy próbuję: -

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

uzyskać wyjątek: -

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

Wydaje jest to związane z https://github.com/AutoMapper/AutoMapper/issues/159 - choć jestem już przy użyciu typu złożonego dla kolekcji dziecięcej. Zgaduję, że CreateMapExpression nie obsługuje OrderBy w kolekcjach potomnych?

Jeśli nie używam .project() do(), a następnie można łatwo sortować kolekcję dziecięcą: -.

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

ale potem muszę powtórzyć mapowanie gdziekolwiek chcę go używać, pokonując cel użycia AutoMappera.

Co ciekawe: -

1) mogę wykonywać inne (bardziej skomplikowane) operacji na zbiorach dzieci i spłaszczyć tych do mojego rodzica DTO żadnego problemu: - mogę Mapper.Map<FooDto>(foo);

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

2) w pamięci w porządku, a sortowanie barów nie stanowi problemu.

Możliwe jest sortowanie kolekcji podrzędnej na poziomie IQueryable przy jednoczesnym użyciu funkcji .Project(). Do()?

Odpowiedz

11

Skończyło się na modyfikację kodu źródłowego AutoMapper aby wesprzeć ten scenariusz.Mam nadzieję, że proponowana poprawka zostanie przyjęta, ale w międzyczasie można zobaczyć szczegóły na: -

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

+1

Żądanie pobrania zostało zaakceptowane, a zmiana jest teraz w automapper 3. –

+0

Używam AutoMapper 3.3.1, pozwala to na porządek w konfiguracji programu odwzorowującego, ale tak naprawdę nie jest realizowane przez. dane nie są sortowane, a profiler SQL nie pokazuje żadnego zamówienia w wygenerowanym sql. czego mi brakuje? – mendel

+0

Może to być błąd wprowadzony od wersji 3.0, lub może być problem z twoją konfiguracją. Opublikuj [MCVE] (http://stackoverflow.com/help/mcve) jako nowe pytanie, link do niego tutaj, a ja zajrzę? –

0

Zastanawiam się, czy sortowanie pasków wewnątrz kwerendy podczas mapowania jest najlepszym miejscem do tego? Czy umieszczenie sortowania na mapie nie oznacza, że ​​sortuje się w niewłaściwym czasie?

Na przykład, jeśli chcesz wiedzieć, czy kolekcja Bars zawiera 1 pozycję? Wywoływanie FooDto.Bars.Any() nadal powodowałoby sortowanie w bazie danych.

Być może byłoby lepiej mieć inną nieruchomość w FooDTO (ignorowane w mapowaniu), który wygląda tak:

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

      return Bars.OrderBy(x => x.SortOrder).ToList(); 
     } 
    } 
+0

myślę, że może potencjalnie wykorzystać „AfterMap” metody na rzeczy konfiguracji mapowanie AutoMapper aby wykonać sortowanie ? –

+0

Colin: Jestem mało prawdopodobne, aby pobrać cały DTO tylko po to, aby zobaczyć, czy FooDto.Bars.Any() - to specyficzne zapytanie dla określonego widoku. –

+0

Richard B. Spojrzałem na AfterMap, a wykonam zadanie (podobnie jak własność Colin's "SortedBars"), ale mając profilowane, wolę, aby sortowanie odbywało się w bazie danych, tak jak to robię, gdy robię . Wybierz (x => nowy FooDto ...) –