2013-02-14 9 views
14

Ho un oggetto grafico che sto caricando da un database utilizzando EF CodeFirst e automapper in DTOs: -.automapper Progetto() A() e ordinamento una collezione bambino

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

mie mappature assomigliano: -

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

Finora, tutto bene. Posso afferrare le entità dal mio contesto e progetto al DTO bene: -

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

Quello che non posso fare con questo approccio, tuttavia, è una sorta il Bars dal loro SortOrder all'interno del IQueryable.

Se provo: -

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

ottengo un'eccezione: -

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

Sembra che questa è legata alla https://github.com/AutoMapper/AutoMapper/issues/159 - se sto già utilizzando un tipo complesso per la raccolta bambino. Immagino che CreateMapExpression non supporti OrderBy sulle raccolte figlio?

Se io non sto usando .project() A() allora posso ordinare la collezione bambino facilmente: -.

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

ma poi devo ripetere la mappatura ovunque io voglio usarlo, sconfiggendo lo scopo di usare AutoMapper.

Curiosamente: -

1) Posso eseguire altre operazioni (più complicato) sulla raccolta del bambino e appiattire quelle nel mio genitore DTO nessun problema: -?

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

2) posso Mapper.Map<FooDto>(foo); in memoria va bene, e ordinerà le barre senza problemi.

È possibile ordinare la raccolta figlio al livello IQueryable mentre si utilizza ancora .Project(). To()?

risposta

11

ha finito per modificare il codice sorgente automapper per sostenere questo scenario.Speriamo che la correzione proposta sarà accettata, ma nel frattempo si può vedere i dettagli a: -

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

+1

La richiesta pull è stata accettata e la modifica è ora in automapper 3. –

+0

Uso AutoMapper 3.3.1, consente l'ordinamento nella configurazione del mapper ma in realtà non ordina. i dati non sono ordinati e sql profiler non mostra alcun ordine nello sql generato. Cosa mi manca? – mendel

+0

Potrebbe essere un bug introdotto dalla 3.0 o potrebbe essere un problema con la configurazione. Pubblica un [MCVE] (http://stackoverflow.com/help/mcve) come una nuova domanda, collegalo qui e darò un'occhiata? –

0

Mi chiedo se l'ordinamento delle barre all'interno di Queryable durante la mappatura sia il posto migliore per farlo? Non posizionare l'ordinamento nella mappa significa che esso ordina in momenti inappropriati?

Ad esempio, cosa succede se si volesse sapere se la raccolta Bars contenesse 1 elemento? Chiamando FooDto.Bars.Any() si otterrebbe comunque un ordinamento nel database.

Forse sarebbe meglio avere un'altra proprietà nella FooDTO (ignorato nella mappatura) che assomiglia a questo:

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

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

Penso che si potrebbe potenzialmente utilizzare il metodo "AfterMap" sulle cose configurazione della mappatura del automapper di fare il vostro ordinamento ? –

+0

Colin: È improbabile che estragga tutto il DTO solo per vedere se FooDto.Bars.Any() - è una query specifica per una vista specifica. –

+0

Richard B: Ho guardato AfterMap, e farà il lavoro (come la proprietà "SortedBars" di Colin) dopo una moda, ma avendo profilato preferirei che l'ordinamento avvenga sul database come fa quando lo faccio .Seleziona (x => new FooDto ...) –