AutoMapper:“忽略其余”?
有没有办法告诉AutoMapper忽略除了显式映射的属性之外的所有属性?
我有外部DTO类,可能会从外面改变,我想避免指定每个属性被明确忽略,因为添加新的属性将打破function(导致exception),当试图映射到我自己的对象。
这是我写的一个扩展方法,忽略了目标上的所有不存在的属性。 不知道它是否仍然有用,因为这个问题已经两年多了,但是我遇到了同样的问题,不得不添加大量的手动Ignore调用。
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; }
用法:
Mapper.CreateMap<SourceType, DestinationType>() .IgnoreAllNonExisting();
更新 :显然这不能正常工作,如果你有自定义映射,因为它覆盖它们。 我想如果先调用IgnoreAllNonExisting然后再调用自定义映射,它仍然可以工作。
schdr有一个解决scheme(作为这个问题的答案),它使用Mapper.GetAllTypeMaps()
来找出哪些属性未映射,并自动忽略它们。 看起来像一个更强大的解决scheme给我。
我已经更新了Can Gencer的扩展,不覆盖任何现有的地图。
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; }
用法:
Mapper.CreateMap<SourceType, DestinationType>() .ForMember(prop => x.Property, opt => opt.MapFrom(src => src.OtherProperty)) .IgnoreAllNonExisting();
我已经能够以下面的方式做到这一点:
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*/); ...
注意:我正在使用AutoMapper v.2.0。
从我所理解的问题是,有目的地的领域没有在源的映射字段,这就是为什么你正在寻找方式来忽略这些非映射的目标字段。
而不是实现和使用这些扩展方法,你可以简单地使用
Mapper.CreateMap<destinationModel, sourceModel>(MemberList.Source);
现在,automapper知道它只需要validation所有的源字段是映射的,而不是相反的。
从AutoMapper 5.0开始, .TypeMap
上的.TypeMap
属性消失了,这意味着4.2解决scheme不再有效。 我已经创build了一个解决scheme,使用原始的function,但使用不同的语法:
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>(); }
执行:
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)); } }
这个问题已经有几年了,但是使用目前版本的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; }
AutoMapper的5.0.0-beta-1版本引入了ForAllOtherMembers
扩展方法,所以你现在可以这样做:
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());
请注意,明确映射每个属性是有好处的,因为当您忘记映射属性时,您将永远无法默默地find映射失败的问题。
也许在你的情况下,忽略所有其他成员,并添加一个TODO
回来,并在这个class的变化频率定下来之后明确这些是明智的。
对于那些在4.2.0及以上版本中使用非静态API的人,可以使用下面的扩展方法(在这里可以在AutoMapperExtensions
类中find):
// 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; }
这里最重要的是一旦静态API被移除,代码如Mapper.FindTypeMapFor
将不再起作用,因此使用expression.TypeMap
字段。
对于Automapper 5.0,为了跳过所有未映射的属性,您只需要放置
.ForAllOtherMembers(X => x.Ignore());
在您的个人资料的末尾。
例如:
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()); } }
在这种情况下,只有输出对象的Id字段将被parsing,所有其他的将被跳过。 像魅力一样工作,似乎我们不再需要任何棘手的扩展!
我已经更新了Robert Schroeder对AutoMapper 4.2的回答。 使用非静态映射器configuration,我们不能使用Mapper.GetAllTypeMaps()
,但expression
对所需的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; }
您希望如何指定某些成员被忽略? 是否有您想申请的惯例,基础类别或属性? 一旦你明确指定了所有的映射,我不确定你会从AutoMapper中获得什么价值。
这似乎是一个古老的问题,但认为我会发布我的答案给其他人看起来像我一样。
我使用ConstructUsing,与ForAllMembers结合的对象初始值设定项忽略例如
Mapper.CreateMap<Source, Target>() .ConstructUsing( f => new Target { PropVal1 = f.PropVal1, PropObj2 = Map<PropObj2Class>(f.PropObj2), PropVal4 = f.PropVal4 }) .ForAllMembers(a => a.Ignore());
关于忽略许多成员的唯一信息是这个线程 – http://groups.google.com/group/automapper-users/browse_thread/thread/9928ce9f2ffa641f 。 我认为你可以使用ProvideCommonBaseClassConfiguration中的技巧忽略类似类的常见属性。
而且没有关于“忽略其余”function的信息。 我已经看过以前的代码,在我看来,添加这样的function将是非常非常困难的。 你也可以尝试使用一些属性,并用它忽略属性标记,并添加一些通用/通用代码来忽略所有标记的属性。
在3.3.1版本中,您只需使用IgnoreAllPropertiesWithAnInaccessibleSetter()
或IgnoreAllSourcePropertiesWithAnInaccessibleSetter()
方法即可。
您可以使用ForAllMembers,而不是像这样只需要覆盖
public static IMappingExpression<TSource, TDest> IgnoreAll<TSource, TDest>(this IMappingExpression<TSource, TDest> expression) { expression.ForAllMembers(opt => opt.Ignore()); return expression; }
要小心,它会忽略所有,如果你不添加自定义映射,他们已经被忽略,将无法正常工作
另外,我想说,如果你有unit testing的AutoMapper。 你testing所有的属性映射正确的所有模型,你不应该使用这种扩展方法
你应该写明确的忽略
我知道这是一个古老的问题,但@jmoerdyk在你的问题:
你将如何在configuration文件中的链接的CreateMap()expression式中使用它?
你可以在Profile ctor中使用这个答案
this.IgnoreUnmapped(); CreateMap<TSource, Tdestination>(MemberList.Destination) .ForMember(dest => dest.SomeProp, opt => opt.MapFrom(src => src.OtherProp));