12

Is there anyway of Automapper to ignore all properties of a certain type? We are trying to improve the quality of our code by validating the Automapper mappings but having to put an .Ignore() for all IEnumerable<SelectListItem> which are always manually created is creating friction and slowing down development.

Any ideas?

Possible Idea after creating mappings:

    var existingMaps = Mapper.GetAllTypeMaps();
    foreach (var property in existingMaps)
    {
        foreach (var propertyInfo in property.DestinationType.GetProperties())
        {
            if (propertyInfo.PropertyType == typeof(List<SelectListItem>) || propertyInfo.PropertyType == typeof(IEnumerable<SelectListItem>))
            {
                property.FindOrCreatePropertyMapFor(new PropertyAccessor(propertyInfo)).Ignore();
            }
        }
    }
GraemeMiller
  • 11,973
  • 8
  • 57
  • 111

2 Answers2

18

Automapper currently does not support type based property ignores.

Currently there is three ways to ignore properties:

  • Use the Ignore() options when creating your mapping

    Mapper.CreateMap<Source, Dest>()
        .ForMember(d => d.IgnoreMe, opt => opt.Ignore());
    

    this is what you want to avoid.

  • Annotate on the your IEnumerable<SelectListItem> properties with the the IgnoreMapAttribute

  • If your IEnumerable<SelectListItem> property names follow some naming convention. E.g. all them start with the word "Select" you can use the AddGlobalIgnore method to ignore them globally:

    Mapper.Initialize(c => c.AddGlobalIgnore("Select"));
    

    but with this you can only match with starts with.

However you can create a convinience extension method for the first options which will automatically ignore the properties of a given type when you call CreateMap:

public static class MappingExpressionExtensions
{
    public static IMappingExpression<TSource, TDest> 
        IgnorePropertiesOfType<TSource, TDest>(
        this IMappingExpression<TSource, TDest> mappingExpression,
        Type typeToIgnore
        )
    {
        var destInfo = new TypeInfo(typeof(TDest));
        foreach (var destProperty in destInfo.GetPublicWriteAccessors()
            .OfType<PropertyInfo>()
            .Where(p => p.PropertyType == typeToIgnore))
        {
            mappingExpression = mappingExpression
                .ForMember(destProperty.Name, opt => opt.Ignore());
        }

        return mappingExpression;
    }
}

And you can use it with the following way:

Mapper.CreateMap<Source, Dest>()
    .IgnorePropertiesOfType(typeof(IEnumerable<SelectListItem>));

So it still won't be a global solution, but you don't have to list which properties need to be ignored and it works for multiple properties on the same type.

If you don't afraid to get your hands dirty:

There is currently a very hacky solution which goes quite deep into the internals of Automapper. I don't know how public is this API so this solution might brake in the feature:

You can subscribe on the ConfigurationStore's TypeMapCreated event

((ConfigurationStore)Mapper.Configuration).TypeMapCreated += OnTypeMapCreated;

and add the type based ignore directly on the created TypeMap instances:

private void OnTypeMapCreated(object sender, TypeMapCreatedEventArgs e)
{
    foreach (var propertyInfo in e.TypeMap.DestinationType.GetProperties())
    {
        if (propertyInfo.PropertyType == typeof (IEnumerable<SelectListItem>))
        {
            e.TypeMap.FindOrCreatePropertyMapFor(
                new PropertyAccessor(propertyInfo)).Ignore();
        }
    }
}
nemesv
  • 138,284
  • 16
  • 416
  • 359
  • Thanks yeah I had found AddGlobalIgnore but wanted to do it on type. IgnorePropertiesOfType looks good thanks. Just annoying we have to add it to hundreds of VMs. So there is no way to search all mapped models and via reflection find properties with type of IEnumerable and then apply the ignore attribute? – GraemeMiller Mar 11 '13 at 13:57
  • To add attributes dynamically you will something which injects IL like Postharp. However I've found a rather dirty/hacky golbal solution. I will update my answer. – nemesv Mar 11 '13 at 14:17
  • Yeah was just looking at the source. I saw where GlobalIgnore was pulled in was thinking of changing the source for us but this looks like it will do the trick! Thanks – GraemeMiller Mar 11 '13 at 14:24
  • Of course because Automapper is open source you can fork the project and add your features. You can also ask for this is as feature on Github. Or if the `TypeMapCreated` is working for you then just use that. – nemesv Mar 11 '13 at 14:29
  • I think I found another way. Looks like it works. Any thoughts? I updated my questions, based on your code but I just run it after mappings – GraemeMiller Mar 11 '13 at 14:52
  • Yeah `Mapper.GetAllTypeMaps` could be fine if you can have one central place and time when all your mappings are created. However if you add mappings multiple places at different times you are better with the `TypeMapCreated` event. – nemesv Mar 11 '13 at 15:22
  • @nemesv once i use Mapper.Initialize(c => c.AddGlobalIgnore("Select")); all my errors are gone even for fields which do not match the "Select" string in any manner. Am i missing something? – Ram Jun 19 '15 at 05:08
  • with multiple mappers you must you the solution in the answer not the one in the question – Master Azazel Jan 13 '17 at 14:53
1

If you come across this now, it appears there is another way.

Mapper.Initialize(cfg =>
{
    cfg.ShouldMapProperty = pi => pi.PropertyType != typeof(ICommand);
});

I did not look into when this was introduced. This appears it will block or allow for however you filter this. See this: AutoMapper Configuration

Chris Culver
  • 38
  • 1
  • 7