Conditional Mapping from Parameter in Automapper

1.1k Views Asked by At

I want to pass a paramether to a mapping so then it decides a value based on that parameter.

A simple class

public class Person
{
  string Name;
}

And its DTO

public class PersonDTO
{
  string Name;
  string IsAProgrammer;
}

I want something Like this

var parameterIsTrue = true; // how do I pass this value?

CreateMap<MyClassDTO, MyClass>()
  .ForMember(x => x.Name, opt => opt.MapFrom(src => src.Name))
  .ForMember(x => x.IsAProgrammer, 
               opt => opt.MapFrom(parameterIsTrue ? "sure!" : "nope"));

EDIT

I am Trying to write a Custom Resolver but can't figure how to pass arguments to it. I figured how to make the resolver:

public class PersonResolver : IValueResolver<Person, PersonDTO, string>
{
  ...
  public string Resolve(Person src, 
                        PersonDTO dest, 
                        string destMember, 
                        ResolutionContext context)
  {
    return (bool) context.Items["isProgrammer"] ? "sure!" : "nope";
  }
}

But I'm still failing with the mapping:

CreateMap<MyClassDTO, MyClass>()
  .ForMember(x => x.Name, opt => opt.MapFrom(src => src.Name))
  .ForMember(x => x.IsAProgrammer, 
               opt => opt.ResolveUsing(PersonResolver)); // what ???
               // how do I pass the context?
1

There are 1 best solutions below

0
On

I found a solution to this problem that worked for my use case. I had details on an object I didn't want to load in some conditions.

I created this extension method for easy reuse for all the properties that met this criteria:


/// <summary>
/// Don't map the field if the provided <paramref name="keyName"/> is set to <see cref="false"/>. Defaults to true otherwise
/// </summary>
public static IMemberConfigurationExpression<TSource, TDestination, TMember> 
  IgnoreIfParam<TSource, TDestination, TMember>(
    this IMemberConfigurationExpression<TSource, TDestination, TMember> config, 
    string keyName)
{
    config
        .PreCondition(rc =>
        {
            if (rc.Options.Items.TryGetValue(keyName, out var performMapping) && performMapping is bool b)
            {
                return b;
            }
            return true;
        });
    return config;
}

In your mapping profile, you can use the above extension method like so:

public const string IncludeDetail = "IncludeDetail";

public MyProfile() {
 CrateMap<Source, Dest>()
    //for a member with a specified MapFrom():
    .ForMember(x => x.Media, c => c.IgnoreIfParam(IncludeProductDetail).MapFrom(x => x.Detail.Media))
    
    //for a member with implied MapFrom() on the source, you don't need MapFrom()
    .ForMember(x => x.Name, c => c.IgnoreIfParam(MyProfile.IncludeDetail))

Then to set this condition when mapping, I created another helper extension method:

public static IMappingOperationOptions<object, TDestination> 
  SetOption<TDestination>(
    this IMappingOperationOptions<object, TDestination> options, 
    string key, bool val)
{
    options.Items[key] = val;
    return options;
}

Which then you can call while mapping like so:

var res = _mapper.Map<MyView>(model, o => o
    .SetOption(MyProfile.IncludeDetail, false)
);
return res;