Craig H
Craig H

Reputation: 2071

AutoMapper conditionally map a property based on an external value

I have search results being returned from an API search method and I would like to keep the response content length as short as possible.

I also have AutoMapper set up, and that in itself is all working nicely with various mappings configured in a profile.

One of the properties of a search result could be fairly weighty and I don't want to have to include that data as it is not likely to be always required. So on the search criteria I have added a flag to include this property.

Is there a way to conditionally map a property based on this other external factor?

At the moment, in the map configuration I have told it to ignore the weighty property, and then if the criteria specifies it, I am subsequently mapping another collection and assigning it to the search result.

e.g. In the mapping profile:

this.CreateMap<myModel, myDto>()
    .ForMember((dto) => dto.BigCollection,
               (opt) => opt.Ignore())

and then in the code:

results.MyDtos = myModels.Select((m) => Mapper.Map<myDto>(m));
if (searchCriteria.IncludeBigCollection)
{
    foreach(MyDto myDto in results.MyDtos)
    {
        // Map the weighty property from the appropriate model.
        myDto.BigCollection = ...
    }
}

Upvotes: 2

Views: 3421

Answers (1)

MaKCbIMKo
MaKCbIMKo

Reputation: 2820

If you are using Automapper 5.0 then you can use IMappingOperationOptions and IValueResolver to pass the values from the method scope to the mapper itself.

Here is an example:

Your Value Resolver:

class YourValueResolver : IValueResolver<YourSourceType, YourBigCollectionType>
{
    public YourBigCollectionType Resolve(YourSourceType source, YourBigCollectionType destination, ResolutionContext context)
    {
        // here you need your check
        if((bool)context.Items["IncludeBigCollection"])
        {
            // then perform your mapping
            return mappedCollection;
        }
        // else return default or empty
        return default(YourBigCollectionType);
    }
}

Configure your mappings:

new MapperConfiguration(cfg =>
{
    cfg.CreateMap<YourSourceType, YourDestinationType>().ForMember(d => d.YourBigCollection, opt => opt.ResolveUsing<YourValueResolver>()); 
});

And then you can call Map method like:

mapper.Map<YourDestinationType>(yourSourceObj, opt.Items.Add("IncludeBigCollection", IncludeBigCollectionValue));

IncludeBigCollectionValue will be passed into value resolver and used according what you wrote in there.

Upvotes: 3

Related Questions