AutoMapper: “Ignorer le rest”?

Existe-t-il un moyen d’indiquer à AutoMapper d’ignorer toutes les propriétés, à l’exception de celles qui sont explicitement mappées?

J’ai des classes DTO externes qui sont susceptibles de changer de l’extérieur et je veux éviter de spécifier chaque propriété à ignorer explicitement, car l’ajout de nouvelles propriétés va casser la fonctionnalité (provoquer des exceptions) lorsque vous essayez de les mapper dans mes propres objects.

C’est une méthode d’extension que j’ai écrite qui ignore toutes les propriétés non existantes sur la destination. Je ne sais pas si cela sera toujours utile car la question a plus de deux ans, mais j’ai rencontré le même problème en ajoutant beaucoup d’appels manuels.

 public static IMappingExpression IgnoreAllNonExisting (this IMappingExpression 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; } 

Usage:

 Mapper.CreateMap() .IgnoreAllNonExisting(); 

MISE À JOUR : Apparemment, cela ne fonctionne pas correctement si vous avez des mappages personnalisés, car ils les écrasent. Je suppose que cela pourrait encore fonctionner si vous appelez d’abord IgnoreAllNonExisting, puis les mappages personnalisés plus tard.

schdr a une solution (en réponse à cette question) qui utilise Mapper.GetAllTypeMaps() pour déterminer les propriétés non mappées et les ignorer automatiquement. Cela me semble une solution plus robuste.

J’ai mis à jour l’extension de Can Gencer pour ne pas écraser les cartes existantes.

 public static IMappingExpression IgnoreAllNonExisting(this IMappingExpression 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; } 

Usage:

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

D’après ce que j’ai compris, la question était qu’il y a des champs sur la destination qui n’ont pas de champ mappé dans la source, ce qui explique pourquoi vous cherchez à ignorer ces champs de destination non mappés.

Au lieu d’implémenter et d’utiliser ces méthodes d’extension, vous pouvez simplement utiliser

 Mapper.CreateMap(MemberList.Source); 

Maintenant, l’automapper sait qu’il doit uniquement valider que tous les champs source sont mappés mais pas l’inverse.

J’ai pu faire cela de la façon suivante:

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

Remarque: j’utilise AutoMapper v.2.0.

À partir de AutoMapper 5.0, la propriété .TypeMap sur IMappingExpression disparu, ce qui signifie que la solution 4.2 ne fonctionne plus. J’ai créé une solution qui utilise la fonctionnalité d’origine mais avec une syntaxe différente:

 var config = new MapperConfiguration(cfg => { cfg.CreateMap(); cfg.IgnoreUnmapped(); // Ignores unmapped properties on all maps cfg.IgnoreUnmapped(); // Ignores unmapped properties on specific map }); // or add inside a profile public class MyProfile : Profile { this.IgnoreUnmapped(); CreateMap(); } 

La mise en oeuvre:

 public static class MapperExtensions { private static void IgnoreUnmappedProperties(TypeMap map, IMappingExpression expr) { foreach (ssortingng 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 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(this IProfileExpression profile) { profile.IgnoreUnmapped(typeof(TSrc), typeof(TDest)); } } 

La version 5.0.0-beta-1 d’AutoMapper introduit la méthode d’extension ForAllOtherMembers pour que vous puissiez maintenant faire ceci:

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

Sachez que le mappage explicite de chaque propriété présente un avantage, car vous ne rencontrerez jamais de problèmes de mappage en mode silencieux lorsque vous oubliez de mapper une propriété.

Peut-être dans votre cas, il serait sage d’ignorer tous les autres membres et d’append un TODO pour revenir en arrière et les rendre explicites une fois la fréquence des modifications de cette classe établie.

Il y a quelques années que la question a été posée, mais cette méthode d’extension me semble plus propre, en utilisant la version actuelle d’AutoMapper (3.2.1):

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

Pour ceux qui utilisent l’ API non statique en version 4.2.0 et supérieure, la méthode d’extension suivante (trouvée ici dans la classe AutoMapperExtensions ) peut être utilisée:

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

L’important est qu’une fois que l’API statique est supprimée, le code tel que Mapper.FindTypeMapFor ne fonctionnera plus, d’où l’utilisation du champ expression.TypeMap .

Pour Automapper 5.0 afin de sauter toutes les propriétés non mappées, vous avez juste besoin de mettre

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

à la fin de votre profil.

Par exemple:

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

Dans ce cas, seul le champ Id pour l’object de sortie sera résolu, tous les autres seront ignorés. Fonctionne comme un charme, semble que nous n’avons plus besoin d’extensions délicates!

J’ai mis à jour la réponse de Robert Schroeder pour AutoMapper 4.2. Avec les configurations de mappeur non statiques, nous ne pouvons pas utiliser Mapper.GetAllTypeMaps() , mais l’ expression a une référence à TypeMap requirejse:

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

Comment préféreriez-vous spécifier que certains membres soient ignorés? Existe-t-il une convention, une classe de base ou un atsortingbut que vous souhaitez appliquer? Une fois que vous avez commencé à spécifier explicitement tous les mappages, je ne suis pas certain de la valeur que vous obtiendrez sur AutoMapper.

Cela semble être une vieille question mais je pensais que je posterais ma réponse pour quiconque ressemblerait à moi.

J’utilise ConstructUsing, l’initialiseur d’object associé à ForAllMembers ignore par exemple

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

La seule chose à propos de l’ignorance de nombreux membres est ce thread – http://groups.google.com/group/automapper-users/browse_thread/thread/9928ce9f2ffa641f . Je pense que vous pouvez utiliser l’astuce utilisée dans ProvidingCommonBaseClassConfiguration pour ignorer les propriétés communes pour des classes similaires.
Et il n’y a pas d’informations sur la fonctionnalité “Ignorer le rest”. J’ai déjà regardé le code et il me semble très difficile d’append de telles fonctionnalités. Vous pouvez également essayer d’utiliser des atsortingbuts et des marques avec des propriétés ignorées et append du code générique / commun pour ignorer toutes les propriétés marquées.

Vous pouvez utiliser ForAllMembers, que le remplacement par écrasement est uniquement nécessaire

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

Attention, tout sera ignoré, et si vous n’ajoutez pas de mappage personnalisé, ils sont déjà ignorés et ne fonctionneront pas

aussi, je veux dire, si vous avez un test unitaire pour AutoMapper. Et vous testez que tous les modèles avec toutes les propriétés mappées correctement, vous ne devriez pas utiliser une telle méthode d’extension

vous devriez écrire ignorer explicitement

Je sais que c’est une vieille question, mais @jmoerdyk dans votre question:

Comment utiliseriez-vous cela dans une expression CreateMap chaînée dans un profil?

vous pouvez utiliser cette réponse comme ça dans le profil ctor

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

Dans la version 3.3.1, vous pouvez simplement utiliser les IgnoreAllPropertiesWithAnInaccessibleSetter() ou IgnoreAllSourcePropertiesWithAnInaccessibleSetter() .