2015-05-05 17 views
5

So che sono state poste varie domande che assomigliano a questa domanda, ma per quanto posso dire (e testare), nessuna delle soluzioni fornite sembra adattarsi, quindi ecco qui.Denormalizza la gerarchia di oggetti con automapper

Mi chiedo se sia possibile appiattire/denormalizzare una gerarchia di oggetti in modo che un'istanza con un elenco di proprietà nidificate sia mappata a un elenco di alcuni tipi di destinazione utilizzando AutoMapper.

ho una classe di origine che sembra qualcosa di simile

Fonti:

public class DistributionInformation 
{ 
    public string Streetname; 
    public RouteInformation[] Routes; 
} 

public class RouteInformation 
{ 
    public int RouteNumber; 
    public string RouteDescription; 
} 

Destinazione:

public class DenormDistributionInfo 
{ 
    public string Streetname; 
    public int RouteNumber; 
    public string RouteDescription; 
} 

così voglio mappare le due fonti a un elenco di destinazione denormalizzato DenormDistributionInfo.

cioè:

IEnumerable<DenormDistributionInfo> result = Mapper.Map(distributionInformationInstance); 

Che è possibile/fattibile utilizzando automapper, o dovrei cedere e denormalise esso "manualmente"?

+0

IMO, l'utilizzo di Automapper oltre il caso molto semplice di tipi di mapping con proprietà identiche tra loro è un abuso di Automapper.La tentazione diventa nascondere tutti i tipi di logica dell'applicazione nei mapping, a quel punto, hai creato un sistema più difficile da capire rispetto a un approccio più convenzionale. (Divulgazione: Non mi piace Automapper ... [Gli amici non lasciano che gli amici usino Automapper] (http://www.uglybugger.org/software/post/friends_dont_let_friends_use_automapper).) – spender

risposta

4

L'elemento principale è che si desidera evitare di dover "cercare" i dati nella mappatura che non sono impliciti nell'origine. Le mappature "magiche" causano gravi problemi di manutenzione lungo la linea.

Concettualmente, tuttavia, questa mappatura è piuttosto semplice. L'unico fattore complicato è che sono necessari due oggetti di origine (sia uno DistributionInformation sia uno RouteInformation) per costruire l'oggetto di destinazione. Se si segue quel treno di pensiero, possiamo creare una mappatura non magico che conserva chiaramente il nostro intento - ecco come lo farei: -

// We need both source objects in order to perform our map 
Mapper.CreateMap<Tuple<DistributionInformation, RouteInformation>, DenormDistributionInfo>() 
     .ForMember(d => d.Streetname, o => o.MapFrom(s => s.Item1.Streetname)) 
     .ForMember(d => d.RouteDescription, o => o.MapFrom(s => s.Item2.RouteDescription)) 
     .ForMember(d => d.RouteNumber, o => o.MapFrom(s => s.Item2.RouteNumber)); 

// We can use ConstructUsing to pass both our source objects to our map 
Mapper.CreateMap<DistributionInformation, IEnumerable<DenormDistributionInfo>>() 
     .ConstructUsing(
      x => x.Routes 
       .Select(y => Mapper.Map<DenormDistributionInfo>(Tuple.Create(x, y))) 
       .ToList()); 

E per invocarlo: -

var flattened = Mapper.Map<IEnumerable<DenormDistributionInfo>>(source); 

È possibile evitare un po 'dell'horror Tuple, se lo si desidera, creando un DTO per contenere entrambi gli oggetti sorgente. Consiglio vivamente questo in particolare se il tuo codice reale è leggermente più impegnato rispetto all'esempio che hai presentato nella tua domanda.

Se utilizzare o meno AutoMapper per eseguire questa mappatura è più o meno complicato di una semplice operazione manuale, dipende da voi decidere. In questo caso, non credo che mi preoccuperei, ma in uno scenario più complesso che viene ripetuto spesso potrei considerarlo.

+0

Okay capito. Sembra che nel mio caso potrei anche mapparlo manualmente, in ogni caso il numero di eccezioni dalla convenzione significa che è fastidioso a prescindere. Grazie per le informazioni. –

0

ho scavato dentro un po ', e mentre ho scelto di risolvere il problema attraverso la mappatura "manualmente" c'è un altro modo (a parte la risposta inviato da Iain. Ci si sente un po' hacky però.

Il idea è quella di utilizzare un convertitore di tipi e mappare due volte

public class DistributionInfoConverter : ITypeConverter<DistributionInformation, IEnumerable<DenormDistributionInfo>> 
    { 
     public IEnumerable<DenormDistributionInfo> Convert(ResolutionContext context) 
     { 
      var result = new List<DenormDistributionInfo>(); 
      var source = (DistributionInformation)context.SourceValue; 

      foreach (var routeDetail in source.Routes) 
      { 
       var model = new DenormDistributionInfo(); 
       Mapper.Map(routeDetail, model); 
       Mapper.Map(source, model); 
       result.Add(model); 
      } 

      return result; 
     } 
    } 

    Mapper.CreateMap<RouteInformation, DenormDistributionInfo>(); 
    Mapper.CreateMap<DistributionInformation, DenormDistributionInfo>() 
    Mapper.CreateMap<DistributionInformation, IEnumerable<DenormDistributionInfo>>().ConvertUsing<DistributionInfoConverter>(); 

L'unico problema è che per le collezioni di DistributionInformation si devono ciclo/selezionare ogni voce e piano invece di lasciare che figura automapper il modo di mappare una collezione ad un collezione come faresti normalmente.