AutoMapper: "Ignora il resto"?


206

C'è un modo per dire ad AutoMapper di ignorare tutte le proprietà tranne quelle che sono mappate esplicitamente?

Ho classi DTO esterne che probabilmente cambieranno dall'esterno e voglio evitare di specificare ogni proprietà da ignorare esplicitamente, poiché l'aggiunta di nuove proprietà interromperà la funzionalità (causa eccezioni) quando provo a mapparle nei miei oggetti.


1
con ValueInjecter valueinjecter.codeplex.com/documentation crei ValueInjections che hanno il loro algoritmo di mappatura e mappa tra proprietà specifiche e non si preoccupano del resto delle proprietà
Omu

24
Per coloro che usano Automapper> versione 5, scorri verso il basso per vedere i dettagli delle risposte.ForAllOtherMembers(opts => opts.Ignore())
Jack Ukleja,

@Schneider ".ForAllOtherMembers (opts => opts.Ignore ())" è diverso con l'estensione "IgnoreAllNonExisting" qui, la differenza principale è se non hai configurato la proprietà esplicitamente, con ".ForAllOtherMembers (opts => opts.Ignore ( )) "non otterrai nulla di mappato. usa "IgnoreAllNonExisting" senza la proprietà di configurazione esplicita, ottieni comunque alcune proprietà mappate (proprietà con lo stesso nome) con valore.
Drago

Sì. ForAllOtherMembers è la risposta. Le risposte IgnoreUnmapped non fanno altro se non far passare config-valid-assert, perché i membri non mappati vengono comunque ignorati.
N73k,

Vale la pena notare che quando si esegue questa operazione, si nascondono esplicitamente cambiamenti potenzialmente rilevanti o importanti nelle classi da mappare. Avere mappature esplicite per ogni proprietà ti lascerà con un test interrotto ogni volta che la classe mappata cambia, costringendoti a valutarla correttamente. (Dato che hai un test che fa la AssertConfigurationIsValid()chiamata) Per questo motivo, considero "Ignora il resto" un antipasto.
Arve Systad,

Risposte:


83

Questo è un metodo di estensione che ho scritto che ignora tutte le proprietà non esistenti sulla destinazione. Non sono sicuro se sarà ancora utile poiché la domanda ha più di due anni, ma ho riscontrato lo stesso problema con l'aggiunta di molte chiamate Ignore manuali.

public static IMappingExpression<TSource, TDestination> IgnoreAllNonExisting<TSource, TDestination>
(this IMappingExpression<TSource, TDestination> expression)
{
    var flags = BindingFlags.Public | BindingFlags.Instance;
    var sourceType = typeof (TSource);
    var destinationProperties = typeof (TDestination).GetProperties(flags);

    foreach (var property in destinationProperties)
    {
        if (sourceType.GetProperty(property.Name, flags) == null)
        {
            expression.ForMember(property.Name, opt => opt.Ignore());
        }
    }
    return expression;
}

Uso:

Mapper.CreateMap<SourceType, DestinationType>()
                .IgnoreAllNonExisting();

AGGIORNAMENTO : a quanto pare questo non funziona correttamente se si dispone di mapping personalizzati perché li sovrascrive. Immagino che potrebbe funzionare ancora se prima chiami IgnoreAllNonExisting e poi i mapping personalizzati.

schdr ha una soluzione (come risposta a questa domanda) che usa Mapper.GetAllTypeMaps()per scoprire quali proprietà non sono mappate e ignorarle automaticamente. Mi sembra una soluzione più solida.


Non utilizzo AutoMapper da un po 'di tempo, ma accetterò la tua risposta se funziona per te :).
Igor Brejc,

2
Grazie!! L'ho trovato molto utile. Ignorare le proprietà individualmente stava vanificando lo scopo dell'utilizzo di automapper nella mia situazione.
Daniel Robinson,

Vedi la risposta successiva per uno che non ha il problema di sovrascrittura
Jason Coyne il

3
Questo metodo dovrebbe essere sul codice nativo di autoMapper! Molto bello grazie!
Felipe Oriani,

2
Cordiali saluti, lo stesso Jimmy (autore di AutoMapper) ha commentato di seguito che la risposta di @ nazim è corretta per la versione 5+
Worthy7

244

Da quello che ho capito la domanda era che ci sono campi sulla destinazione che non hanno un campo mappato nella fonte, motivo per cui stai cercando modi per ignorare quei campi di destinazione non mappati.

Invece di implementare e utilizzare questi metodi di estensione potresti semplicemente usare

Mapper.CreateMap<sourceModel, destinationModel>(MemberList.Source);  

Ora l'automapper sa che deve solo convalidare che tutti i campi di origine sono mappati ma non viceversa.

Puoi anche usare:

Mapper.CreateMap<sourceModel, destinationModel>(MemberList.Destination);  

10
Questa risposta dovrebbe avere più voti, forse anche essere contrassegnata come risposta. Ha risolto il mio problema e allo stesso modo MemberList.Destinationavrebbe risolto il problema delle operazioni.
Tedd Hansen,

1
Non funzionerà se si desidera ignorare alcune proprietà sia sull'origine che sulla destinazione :)
RealWillyWoka,

62
Per chiunque venga dopo, QUESTA È LA RISPOSTA CORRETTA PER 5.0
Jimmy Bogard,

3
sembra elegante ma non ha funzionato per me .. ho provato Source and Destination, ma continua a lamentarmi dello stesso oggetto di proprietà mancante di una mappa
Sonic Soul

1
Utilizzando 6.0.2 e questo non funziona periodo. Qualsiasi proprietà non mappata dalla destinazione all'origine, sovrascrive le proprietà nell'origine con valori nulli e 0. Inoltre, il codice non chiarisce cosa stai facendo, specialmente se lavori in gruppo. Ecco perché non mi piace molto questo codice e perché preferisco le parole scelte come la risposta suggerita "IgnoreAllNonExisting"
sksallaj

222

Ho aggiornato l'estensione Can Gencer per non sovrascrivere alcuna mappa esistente.

public static IMappingExpression<TSource, TDestination> 
    IgnoreAllNonExisting<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
{
    var sourceType = typeof (TSource);
    var destinationType = typeof (TDestination);
    var existingMaps = Mapper.GetAllTypeMaps().First(x => x.SourceType.Equals(sourceType) && x.DestinationType.Equals(destinationType));
    foreach (var property in existingMaps.GetUnmappedPropertyNames())
    {
        expression.ForMember(property, opt => opt.Ignore());
    }
    return expression;
}

Uso:

Mapper.CreateMap<SourceType, DestinationType>()
                .ForMember(prop => x.Property, opt => opt.MapFrom(src => src.OtherProperty))
                .IgnoreAllNonExisting();

4
+1, grazie per aver pubblicato questa soluzione. Mi ci sono volute ore per capire strano bug quando uso la soluzione in goo.gl/rG7SL , fino a quando non inciampo di nuovo in questo post.
Nordin,

3
Raccomando il metodo di Yohanb di seguito su questo. Ci sono alcuni casi angolari in cui questo non funziona perché appare.
Jon Barker,

3
Questo può essere fatto in AutoMapper 4.2? ( Mapper.GetAllTypeMaps()È deprecato)
mrmashal,

14
Per AutoMapper versione 5+ basta sostituire Mapper.GetAllTypeMaps()con Mapper.Configuration.GetAllTypeMaps(). Ecco il riferimento github.com/AutoMapper/AutoMapper/issues/1252
Sergey G.

5
Per le nuove persone che leggono questo. Questa risposta è per AutoMapper 2 e al momento della stesura di questo commento siamo alla versione 6. Questo è un trucco e un modo molto più pulito sta usando MemberList enum. Vedi il numero 1839 di Github e una soluzione migliore. github.com/AutoMapper/AutoMapper/issues/1839 Così Esempio: stackoverflow.com/a/31182390/3850405
Ogglas

83

Sono stato in grado di farlo nel modo seguente:

Mapper.CreateMap<SourceType, DestinationType>().ForAllMembers(opt => opt.Ignore());
Mapper.CreateMap<SourceType, DestinationType>().ForMember(/*Do explicit mapping 1 here*/);
Mapper.CreateMap<SourceType, DestinationType>().ForMember(/*Do explicit mapping 2 here*/);
...

Nota: sto usando AutoMapper v.2.0.


4
grazie mille! Esso funziona magicamente. ho provato prima a concatenare le chiamate, ma ForAllMembers restituisce solo un vuoto :(. Non era ovvio che un precedente IgnoreAll potesse essere modificato in seguito.
SeriousM,

5
Nemmeno a me piace così ... se hai 50 membri e vuoi ignorarne 25 .. allora qual è il punto di automapper se devi ancora ignorare 25 membri. Se i nomi corrispondono e ci sono proprietà che non corrispondono ... perché non chiarire a automapper di non corrispondere alle proprietà non mappate e passando tutta la digitazione?
sksallaj,

71

La versione 5.0.0-beta-1 di AutoMapper introduce il ForAllOtherMembersmetodo di estensione, quindi ora puoi farlo:

CreateMap<Source, Destination>()
    .ForMember(d => d.Text, o => o.MapFrom(s => s.Name))
    .ForMember(d => d.Value, o => o.MapFrom(s => s.Id))
    .ForAllOtherMembers(opts => opts.Ignore());

Essere consapevoli del fatto che esiste un vantaggio nel mappare esplicitamente ogni proprietà poiché non si verificheranno mai problemi di mappatura che falliscono in modo silenzioso che si presentano quando si dimentica di mappare una proprietà.

Forse nel tuo caso potrebbe essere saggio ignorare tutti gli altri membri e aggiungere un TODOper tornare e renderli espliciti dopo che la frequenza delle modifiche a questa classe si è stabilizzata.


3
Incredibile, ci sono voluti fino alla versione 5. Guarda quanti voti positivi e tentate risposte a questa domanda ... qualcosa di sbagliato nella governance di Automapper mi chiedo?
Jack Ukleja,

Grazie per questo, mi ci è voluto un po 'per scorrere verso il basso ma questo, ma funziona perfettamente.
Cobolstink,

2
Puoi anche mettere prima la riga ForAllOtherMembers e le cose funzioneranno allo stesso modo, il che è buono se hai un qualche tipo di configurazione della classe base.
N73k,

Questo è ora l'approccio preferito. Ti chiedi se l'OP potrebbe cambiare la risposta accettata?
Chase Florell,

1
Esiste un equivalente per ignorare le proprietà nell'oggetto sorgente? Qualcosa del genere ForAllOtherSourceMembers?
SuperJMN

44

A partire da AutoMapper 5.0, la .TypeMapproprietà on IMappingExpressionè sparita, il che significa che la soluzione 4.2 non funziona più. Ho creato una soluzione che utilizza la funzionalità originale ma con una sintassi diversa:

var config = new MapperConfiguration(cfg =>
{
    cfg.CreateMap<Src, Dest>();
    cfg.IgnoreUnmapped();        // Ignores unmapped properties on all maps
    cfg.IgnoreUnmapped<Src, Dest>();  // Ignores unmapped properties on specific map
});

// or add  inside a profile
public class MyProfile : Profile
{
   this.IgnoreUnmapped();
   CreateMap<MyType1, MyType2>();
}

Implementazione:

public static class MapperExtensions
{
    private static void IgnoreUnmappedProperties(TypeMap map, IMappingExpression expr)
    {
        foreach (string propName in map.GetUnmappedPropertyNames())
        {
            if (map.SourceType.GetProperty(propName) != null)
            {
                expr.ForSourceMember(propName, opt => opt.Ignore());
            }
            if (map.DestinationType.GetProperty(propName) != null)
            {
                expr.ForMember(propName, opt => opt.Ignore());
            }
        }
    }

    public static void IgnoreUnmapped(this IProfileExpression profile)
    {
        profile.ForAllMaps(IgnoreUnmappedProperties);
    }

    public static void IgnoreUnmapped(this IProfileExpression profile, Func<TypeMap, bool> filter)
    {
        profile.ForAllMaps((map, expr) =>
        {
            if (filter(map))
            {
                IgnoreUnmappedProperties(map, expr);
            }
        });
    }

    public static void IgnoreUnmapped(this IProfileExpression profile, Type src, Type dest)
    {
        profile.IgnoreUnmapped((TypeMap map) => map.SourceType == src && map.DestinationType == dest);
    }

    public static void IgnoreUnmapped<TSrc, TDest>(this IProfileExpression profile)
    {
        profile.IgnoreUnmapped(typeof(TSrc), typeof(TDest));
    }
}

3
Come lo useresti in CreateMap<TSource,TDest>()un'espressione incatenata in a Profile?
Jmoerdyk,

2
Grazie per questo. Il metodo GetUnmappedPropertyNames restituisce tutti i nomi di proprietà non mappati, sia sull'origine che sulla destinazione, che sembrano essere interrotti su una mappa inversa, quindi ho dovuto apportare una piccola modifica a IgnoreUnmapped per verificare se la proprietà non mappata era sull'origine o sulla destinazione e ignorare di conseguenza. Ecco un violino che dimostra il problema e l'aggiornamento: dotnetfiddle.net/vkRGJv
Mun

1
Ho aggiornato la mia risposta per includere le tue scoperte: non uso i mapping di origine, quindi non mi ero imbattuto in questo! Grazie.
Richard,

1
Questo non funziona su PCL senza la riflessione disponibile, GetProperty (propName) non esiste.
George Taskos,

Non vedo come questa sia una soluzione alla domanda, o come anche questo faccia qualcosa. Le proprietà non mappate verranno già ignorate, poiché non mappate . Il poster diceva "come si ignorano gli oggetti di scena a meno che non siano esplicitamente mappati". Ciò significa che se ho Src.MyProp e Dest.MyProp, tale mappatura dovrebbe essere ignorata a meno che non ci fosse una chiamata esplicita a MapFrom & ForMember per MyProp. Pertanto, la mappatura predefinita deve essere ignorata. L'unica cosa che fa questa soluzione è far passare la cosa config-valid-assert - che non è comunque necessaria per il funzionamento della mappatura.
N73k,

17

Sono passati alcuni anni da quando è stata posta la domanda, ma questo metodo di estensione mi sembra più pulito, usando l'attuale versione di AutoMapper (3.2.1):

public static IMappingExpression<TSource, TDestination> IgnoreUnmappedProperties<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
{
    var typeMap = Mapper.FindTypeMapFor<TSource, TDestination>();
    if (typeMap != null)
    {
        foreach (var unmappedPropertyName in typeMap.GetUnmappedPropertyNames())
        {
            expression.ForMember(unmappedPropertyName, opt => opt.Ignore());
        }
    }

    return expression;
}

16

Per coloro che utilizzano l' API non statica nella versione 4.2.0 e successive, è possibile utilizzare il seguente metodo di estensione (trovato qui nella AutoMapperExtensionsclasse):

// from http://stackoverflow.com/questions/954480/automapper-ignore-the-rest/6474397#6474397
public static IMappingExpression IgnoreAllNonExisting(this IMappingExpression expression)
{
    foreach(var property in expression.TypeMap.GetUnmappedPropertyNames())
    {
        expression.ForMember(property, opt => opt.Ignore());
    }
    return expression;
}

La cosa importante qui è che una volta rimossa l'API statica, codice come Mapper.FindTypeMapFornon funzionerà più, quindi l'uso del expression.TypeMapcampo.


7
A partire da 5.0, expression.TypeMapnon è più disponibile. Ecco la mia soluzione per 5.0
Richard,

Ho dovuto usare public static IMappingExpression<TSource, TDestination> IgnoreAllNonExisting<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)per risolvere problemi di tipo.
Nick M,

16

Per Automapper 5.0 per saltare tutte le proprietà non mappate è sufficiente inserirle

.ForAllOtherMembers (x => x.Ignore ());

alla fine del tuo profilo.

Per esempio:

internal class AccountInfoEntityToAccountDtoProfile : Profile
{
    public AccountInfoEntityToAccountDtoProfile()
    {
        CreateMap<AccountInfoEntity, AccountDto>()
           .ForMember(d => d.Id, e => e.MapFrom(s => s.BankAcctInfo.BankAcctFrom.AcctId))
           .ForAllOtherMembers(x=>x.Ignore());
    }
}

In questo caso verrà risolto solo il campo ID per l'oggetto di output, tutti gli altri verranno ignorati. Funziona come un fascino, sembra che non abbiamo più bisogno di estensioni complicate!


10

Ho aggiornato la risposta di Robert Schroeder per AutoMapper 4.2. Con le configurazioni del mapper non statico, non possiamo usare Mapper.GetAllTypeMaps(), ma expressionha un riferimento al richiesto TypeMap:

public static IMappingExpression<TSource, TDestination> 
    IgnoreAllNonExisting<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
{
    foreach (var property in expression.TypeMap.GetUnmappedPropertyNames())
    {
        expression.ForMember(property, opt => opt.Ignore());
    }
    return expression;
}

Non funziona in AutoMapper 5.0. La proprietà .TypeMap su IMappingExpression non è disponibile. Per la versione 5. + vedi le estensioni nella risposta di Richard
Michael Freidgeim,

Funziona con AM 4.2
Leszek P

8

Come preferiresti specificare che alcuni membri vengano ignorati? Esiste una convenzione, una classe di base o un attributo che desideri applicare? Una volta che inizi a specificare tutte le mappature in modo esplicito, non sono sicuro di quale valore otterrai da AutoMapper.


Jimmy, hai un punto sull'esplicita '. Per quanto riguarda il modo in cui raggiungere questo obiettivo nel modo più elegante: le classi e gli attributi di base non funzionerebbero in questa situazione, poiché le classi target non sono realmente sotto il mio controllo - sono autogenerate dal contratto dati XSD, quindi si dovrebbe per modificare manualmente questo codice dopo ogni ciclo di generazione. Immagino che la soluzione dipenda da un caso concreto. Forse un'interfaccia fluida simile a quella di Windsor Castle consente di selezionare quali componenti registrare nel contenitore potrebbero essere una soluzione?
Igor Brejc,

Ah, ora ha più senso. Questa è una caratteristica interessante, la vedrò nel tempo 2.1.
Jimmy Bogard,

2
Che ne dici di avere solo un valore configurabile in cui è possibile "ignorare" tutti i campi inesistenti.
Ricardo Sanchez,

6
Questa non è una risposta alla domanda.
user2864740

Ciao Jimmy, sei l'autore, giusto? Vorrei poter ignorare tutte le proprietà inesistenti essendo un comportamento predefinito (può essere controllato da un flag). Inoltre, sto riscontrando uno strano errore di AutoMapper che non riesco a capire. Non mi dà alcun dettaglio.
Naomi,

7

Questa sembra una vecchia domanda, ma ho pensato di pubblicare la mia risposta per chiunque sembri.

Uso ConstructUsing, l'inizializzatore di oggetti accoppiato a ForAllMembers ignora ad es

    Mapper.CreateMap<Source, Target>()
        .ConstructUsing(
            f =>
                new Target
                    {
                        PropVal1 = f.PropVal1,
                        PropObj2 = Map<PropObj2Class>(f.PropObj2),
                        PropVal4 = f.PropVal4
                    })
        .ForAllMembers(a => a.Ignore());

1

L'unica informazione sull'ignorare molti membri è questa discussione: http://groups.google.com/group/automapper-users/browse_thread/thread/9928ce9f2ffa641f . Penso che tu possa usare il trucco usato in ProvidingCommonBaseClassConfiguration per ignorare le proprietà comuni per classi simili.
E non ci sono informazioni sulla funzionalità "Ignora il resto". Ho già esaminato il codice e mi sembra che sarà molto e molto difficile aggiungere tale funzionalità. Inoltre, puoi provare a utilizzare alcuni attributi e contrassegnare con esso le proprietà ignorate e aggiungere un codice generico / comune per ignorare tutte le proprietà contrassegnate.


1
Forse un modo sarebbe quello di utilizzare il metodo ForAllMembers e implementare il mio IMemberConfigurationExpression che riceve una stringa contenente i nomi delle proprietà di quelle proprietà che non dovrebbero essere ignorate, e poi passare attraverso il resto di loro e chiamare Ignore (). Solo un'idea, non sono sicuro che funzionerebbe.
Igor Brejc,

Sì, anche questo può funzionare, ma questo metodo è più complicato dell'uso degli attributi ma offre maggiore flessibilità. È un peccato che non ci siano proiettili d'argento :(.
zihotki il

1

So che questa è una vecchia domanda, ma @jmoerdyk nella tua domanda:

Come lo useresti in un'espressione concatenata di CreateMap () in un profilo?

puoi usare questa risposta in questo modo nel Profile ctor

this.IgnoreUnmapped();
CreateMap<TSource, Tdestination>(MemberList.Destination)
.ForMember(dest => dest.SomeProp, opt => opt.MapFrom(src => src.OtherProp));

0

È possibile utilizzare ForAllMembers, che sovrascrivere è necessario solo in questo modo

public static IMappingExpression<TSource, TDest> IgnoreAll<TSource, TDest>(this IMappingExpression<TSource, TDest> expression)
        {
            expression.ForAllMembers(opt => opt.Ignore());
            return expression;
        }

Fai attenzione, ignorerà tutto e se non aggiungerai il mapping personalizzato, saranno già ignorati e non funzioneranno

Inoltre, voglio dire, se si dispone di unit test per AutoMapper. E testerai che tutti i modelli con tutte le proprietà mappate correttamente non dovresti usare tale metodo di estensione

dovresti scrivere ignora esplicitamente


-1

L'attuale soluzione (versione 9) per ignorare le proprietà che non esistono nel tipo di destinazione consiste nel creare un mapping capovolto e invertirlo:

var config = new MapperConfiguration(cfg => {
  cfg.CreateMap<TheActualDestinationType, TheActualSourceType>().ReverseMap();
});

-2

Nella versione 3.3.1 puoi semplicemente usare IgnoreAllPropertiesWithAnInaccessibleSetter()o IgnoreAllSourcePropertiesWithAnInaccessibleSetter()metodi.


6
Questo non funziona secondo la domanda del poster originale. Questi metodi ignorano solo le proprietà protette o private, non quelle mancanti nell'origine ma presenti nel tipo di destinazione.
Dan,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.