AutoMapper: "Ignore the rest"?

.NetAutomapper

.Net Problem Overview


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.

.Net Solutions


Solution 1 - .Net

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<sourceModel, destinationModel>(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<sourceModel, destinationModel>(MemberList.Destination);  

Solution 2 - .Net

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();

Solution 3 - .Net

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.

Solution 4 - .Net

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.

Solution 5 - .Net

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.

Solution 6 - .Net

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

Solution 7 - .Net

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

Solution 8 - .Net

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!

Solution 9 - .Net

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.

Solution 10 - .Net

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

Solution 11 - .Net

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.

Solution 12 - .Net

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());

Solution 13 - .Net

By default, AutoMapper uses the destination type to validate members but you can skip validation by using MemberList.None option.

var configuration = new MapperConfiguration(cfg =>
  cfg.CreateMap<Source2, Destination2>(MemberList.None);
);

You can find the reference https://docs.automapper.org/en/stable/Configuration-validation.html">here</a>

Solution 14 - .Net

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.

Solution 15 - .Net

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));

Solution 16 - .Net

In a WebApi for dotnet 5, using the Nuget package AutoMapper.Extensions.Microsoft.DependencyInjection, I'm doing it like this in a mapper profile. I'm really rusty with AutoMapper, but it seems to work fine now for unmapped members.

In Startup:

var mapperConfig = new MapperConfiguration(mc => mc.AddProfile(new AutoMapperProfile()));
	services.AddSingleton(mapperConfig.CreateMapper());

and in my AutoMapperProfile:

CreateMap<ProjectActivity, Activity>()
		.ForMember(dest => dest.ActivityName, opt => opt.MapFrom(src => src.Name))
		.ValidateMemberList(MemberList.None);

Solution 17 - .Net

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

Solution 18 - .Net

In version of 3.3.1 you simply can use IgnoreAllPropertiesWithAnInaccessibleSetter() or IgnoreAllSourcePropertiesWithAnInaccessibleSetter() methods.

Solution 19 - .Net

The current (version 9) solution for ignoring the properties that do not exist in the destination type is to create a flipped mapping and reversing it:

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionIgor BrejcView Question on Stackoverflow
Solution 1 - .NetNazim HafeezView Answer on Stackoverflow
Solution 2 - .NetRobert SchroederView Answer on Stackoverflow
Solution 3 - .NetajbeavenView Answer on Stackoverflow
Solution 4 - .NetCan GencerView Answer on Stackoverflow
Solution 5 - .NetYohanbView Answer on Stackoverflow
Solution 6 - .NetRichardView Answer on Stackoverflow
Solution 7 - .NetIravanchiView Answer on Stackoverflow
Solution 8 - .NetframerelayView Answer on Stackoverflow
Solution 9 - .Netnick_wView Answer on Stackoverflow
Solution 10 - .NetmrmashalView Answer on Stackoverflow
Solution 11 - .NetJimmy BogardView Answer on Stackoverflow
Solution 12 - .Netgm1886View Answer on Stackoverflow
Solution 13 - .NetShivkumar HaldikarView Answer on Stackoverflow
Solution 14 - .NetzihotkiView Answer on Stackoverflow
Solution 15 - .Netj.loucao.silvaView Answer on Stackoverflow
Solution 16 - .NetJohan DanforthView Answer on Stackoverflow
Solution 17 - .NetAnatoli KlamerView Answer on Stackoverflow
Solution 18 - .NetIvan KochurkinView Answer on Stackoverflow
Solution 19 - .NetSimopaaView Answer on Stackoverflow