2013-02-14 6 views
14

ich ein Objekt Diagramm haben, dass ich aus einer Datenbank geladen mit EF CodeFirst und AutoMapper in DTOs: -.AutoMapper Project() Bis() und ein Kind Sammlung Sortierung

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

Meine Zuordnungen wie folgt aussehen: -

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

So weit, so gut. Ich kann die Einheiten von meinem Kontext und Projekt zum DTO greifen schön: -

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

Was ich nicht mit diesem Ansatz tun, jedoch ist eine Art der Bars durch ihre SortOrder innerhalb des IQueryable.

Wenn ich versuche: -

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

Ich erhalte eine Ausnahme: -

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

scheint dies zu https://github.com/AutoMapper/AutoMapper/issues/159 verwandt ist - obwohl ich bereits einen komplexen Typ für das Kind Sammlung verwenden. Ich denke, CreateMapExpression unterstützt OrderBy nicht in untergeordneten Sammlungen.

Wenn ich nicht .Projektdaten() verwenden, um(), dann kann es das Kind Sammlung sortiert leicht: -.

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

aber dann muß ich die Zuordnung wiederholen, wo immer ich es benutzen will, den Zweck der Verwendung von AutoMapper zu besiegen.

Merkwürdiger: -

1) Ich kann andere führen (kompliziertere) Operationen auf das Kind Sammlung und glätten diese in meiner Eltern DTO kein Problem: -

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

2) Ich kann Mapper.Map<FooDto>(foo); im Speicher gut, und es wird die Bars kein Problem sortieren.

Es ist möglich, die untergeordnete Sammlung auf der IQueryable-Ebene zu sortieren, während Sie weiterhin .Project() verwenden. To()?

Antwort

11

Beendet die AutoMapper Quellcode zu modifizieren bis dieses Szenario zu unterstützen.Hoffentlich wird die vorgeschlagene Fix wird akzeptiert, aber in der Zwischenzeit können Sie die Details sehen unter: -

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

+1

Die Pull-Anforderung wurde akzeptiert und die Änderung ist jetzt in Autoadapter 3. –

+0

Ich benutze AutoMapper 3.3.1, es ermöglicht die Reihenfolge in der Mapper-Konfiguration, aber es ist nicht wirklich durch bestellen. Die Daten sind nicht sortiert und Sql Profiler zeigt keine Reihenfolge in der generierten SQL. Was vermisse ich? – mendel

+0

Könnte ein Fehler sein, der seit 3.0 eingeführt wurde, oder ein Problem mit Ihrer Konfiguration sein könnte. Poste ein [MCVE] (http://stackoverflow.com/help/mcve) als neue Frage, verlinke hier, und ich schaue es mir an. –

0

Ich frage mich, ob das Sortieren der Bars innerhalb der Queryable während der Zuordnung der beste Ort ist, um dies zu tun? Würde die Sortierung in der Karte nicht bedeuten, dass sie zu unpassenden Zeiten sortiert wird?

Zum Beispiel, was ist, wenn Sie nur wissen wollten, ob die Bars-Sammlung 1 Artikel enthielt? Ein Aufruf von FooDto.Bars.Any() würde immer noch zu einer Sortierung in der Datenbank führen.

Vielleicht wäre es besser, eine andere Eigenschaft in der FooDTO zu haben (in der Abbildung ignoriert), die wie folgt aussieht:

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

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

Ich glaube, Sie könnten möglicherweise die „AfterMap“ -Methode auf dem Kartierungs Konfigurationskram von AutoMapper nutzen Ihre Sortierung zu tun ? –

+0

Colin: Ich werde nicht das gesamte DTO ziehen, nur um zu sehen, ob FooDto.Bars.Any() - es ist eine spezifische Abfrage für eine bestimmte Ansicht. –

+0

Richard B: Ich schaute auf AfterMap, und es wird die Aufgabe erledigen (wie Colins "SortedBars" -Eigenschaft) nach einer Mode, aber nach dem Profilieren würde ich bevorzugen, dass die Sortierung in der Datenbank stattfindet, wie wenn ich es tue .Wählen Sie (x => new FooDto ...) –