可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Is there a way to tell AutoMapper to ignore all of the properties except the ones which are mapped explicitly?
I have external DTO classes which are likely to change from the outside and I want to avoid specifying each property to be ignored explicitly, since adding new properties will break the functionality (cause exceptions) when trying to map them into my own objects.
回答1:
This is an extension method I wrote that ignores all non existing properties on the destination. Not sure if it will still be useful as the question is more than two years old, but I ran into the same issue having to add a lot of manual Ignore calls.
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;
}
Usage:
Mapper.CreateMap<SourceType, DestinationType>()
.IgnoreAllNonExisting();
UPDATE: Apparently this does not work correctly if you have custom mappings because it overwrites them. I guess it could still work if call IgnoreAllNonExisting first and then the custom mappings later.
schdr has a solution (as an answer to this question) which uses Mapper.GetAllTypeMaps()
to find out which properties are unmapped and auto ignore them. Seems like a more robust solution to me.
回答2:
From what I understood the question was that there are fields on the destination which doesn't have a mapped field in the source, which is why you are looking for ways to Ignore those non mapped destination fields.
Instead of implementing and using these extension method you could simply use
Mapper.CreateMap<destinationModel, sourceModel>(MemberList.Source);
Now the automapper knows that it needs to only validate that all the source fields are mapped but not the other way around.
You can also use:
Mapper.CreateMap<destinationModel, sourceModel>(MemberList.Destination);
回答3:
I've updated Can Gencer's extension to not overwrite any existing maps.
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;
}
Usage:
Mapper.CreateMap<SourceType, DestinationType>()
.ForMember(prop => x.Property, opt => opt.MapFrom(src => src.OtherProperty))
.IgnoreAllNonExisting();
回答4:
I've been able to do this the following way:
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*/);
...
Note: I'm using AutoMapper v.2.0.
回答5:
Version 5.0.0-beta-1 of AutoMapper introduces the ForAllOtherMembers
extension method so you can now do this:
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());
Be aware that there is an advantage to explicitly mapping each property as you will never get problems of mapping failing silently that arise when you forget to map a property.
Perhaps in your case it might be wise to ignore all other members and add a TODO
to come back and make these explicit after the frequency of changes to this class settle down.
回答6:
As of AutoMapper 5.0, the .TypeMap
property on IMappingExpression
is gone, meaning the 4.2 solution no longer works. I've created a solution which uses the original functionality but with a different syntax:
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>();
}
Implementation:
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));
}
}
回答7:
There's been a few years since the question has been asked, but this extension method seems cleaner to me, using current version of 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;
}
回答8:
For those who are using the non-static API in version 4.2.0 and above, the following extension method (found here in the AutoMapperExtensions
class) can be used:
// 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;
}
The important thing here is that once the static API is removed, code such as Mapper.FindTypeMapFor
will no longer work, hence the use of the expression.TypeMap
field.
回答9:
For Automapper 5.0 in order to skip all unmapped properties you just need put
.ForAllOtherMembers(x=>x.Ignore());
at the end of your profile.
For example:
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 this case only Id field for output object will be resolved all other will be skipped. Works like a charm, seems we don't need any tricky extensions anymore!
回答10:
I have updated Robert Schroeder's answer for AutoMapper 4.2. With non-static mapper configurations, we can't use Mapper.GetAllTypeMaps()
, but the expression
has a reference to the required 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;
}
回答11:
How would you prefer to specify that certain members be ignored? Is there a convention, or base class, or attribute you would like to apply? Once you get into the business of specifying all the mappings explicitly, I'm not sure what value you'd get out of AutoMapper.
回答12:
This seems an old question but thought I would post my answer for anyone else looking like I was.
I use ConstructUsing, object initializer coupled with ForAllMembers ignore e.g
Mapper.CreateMap<Source, Target>()
.ConstructUsing(
f =>
new Target
{
PropVal1 = f.PropVal1,
PropObj2 = Map<PropObj2Class>(f.PropObj2),
PropVal4 = f.PropVal4
})
.ForAllMembers(a => a.Ignore());
回答13:
The only infromation about ignoring many of members is this thread - http://groups.google.com/group/automapper-users/browse_thread/thread/9928ce9f2ffa641f . I think you can use the trick used in ProvidingCommonBaseClassConfiguration to ignore common properties for similar classes.
And there is no information about the "Ignore the rest" functionality. I've looked at the code before and it seems to me that will be very and very hard to add such functionality. Also you can try to use some attribute and mark with it ignored properties and add some generic/common code to ignore all marked properties.
回答14:
I know this is an old question, but @jmoerdyk
in your question:
How would you use this in a chained CreateMap() expression in a Profile?
you can use this answer like this inside the Profile ctor
this.IgnoreUnmapped();
CreateMap<TSource, Tdestination>(MemberList.Destination)
.ForMember(dest => dest.SomeProp, opt => opt.MapFrom(src => src.OtherProp));
回答15:
You can use ForAllMembers, than overwrite only needed
like this
public static IMappingExpression<TSource, TDest> IgnoreAll<TSource, TDest>(this IMappingExpression<TSource, TDest> expression)
{
expression.ForAllMembers(opt => opt.Ignore());
return expression;
}
Be carefull, it will ignore all, and if you will not add custom mapping, they are already ignore and will not work
also, i want to say, if you have unit test for AutoMapper. And you test that all models with all properties mapped correctly you shouldn't use such extension method
you should write ignore's explicitly
回答16:
In version of 3.3.1 you simply can use IgnoreAllPropertiesWithAnInaccessibleSetter()
or IgnoreAllSourcePropertiesWithAnInaccessibleSetter()
methods.