1

With Automapper (6.6.2) I'm trying to map a nullable boolean to a destinations object property (dest.IsEnabled.Value). But as soon the source value is null, the entire destination property (dest.IsEnabled) is null. But I only expect the property "value" to be null.

Any idea how to do it right?

class Program
{
    static void Main(string[] args)
    {
        var source = new Source();
        source.IsEnabled = null;

        var config = new MapperConfiguration(cfg =>
        {
            cfg.CreateMap<Source, Dest>();
                //.ConstructUsing(ctor => new Dest());
            cfg.CreateMap<bool?, IsEnabledProperty>()
                .ForMember(dst => dst.Value, opt => opt.MapFrom(src => src));
        });

        //var debug = config.BuildExecutionPlan(typeof(Source), typeof(Dest));

        var mapper = config.CreateMapper();

        //var dest = mapper.Map<Source, Dest>(source, new Dest());
        var dest = mapper.Map<Source, Dest>(source);

        if (dest.IsEnabled == null)
        {
            Console.WriteLine("IsEnabled is null. But why? I expect IsEnabled.Value to be null.");
        }

        Console.ReadLine();
    }
}

class Source
{
    public bool? IsEnabled { get; set; }
}

class Dest
{
    public IsEnabledProperty IsEnabled { get; set; } 
        = new IsEnabledProperty() { IsRequired = true };

    // Just to check if the property is initialized
    public OtherProperty OtherProperty { get; set; }
        = new OtherProperty() { IsRequired = true };
}

class IsEnabledProperty
{
    public bool IsRequired { get; set; }
    public bool? Value { get; set; }
}

class OtherProperty
{
    public bool IsRequired { get; set; }
    public bool? Value { get; set; }
}

Update

When I update the mapping like this

cfg.CreateMap<bool?, IsEnabledProperty>()
   .ConvertUsing((src, dst, context) => 
   {
      if (dst != null) 
         dst.Value = src;
      return dst;
   });

dest.IsEnabled.Value is mapped correctly for all variants (null, false, true), and also the property destination.IsEnabled.IsRequired has the initialized value of true, but it forces me to pass an instance of destination to the mapping method: var dest = mapper.Map<Source, Dest>(source, new Dest());. This doesn't make sense in my eyes, since I expect the destination object should be constructed the same way by automapper?

When looking at the execution plan, I'm asking me, why it checks for (dest == null) in line 11 and not just use the initialized typeMapDestination:

(src, dest, ctxt) =>
{
    Dest typeMapDestination;
    return (src == null)
        ? null
        : {
            typeMapDestination = dest ?? new Dest();
            try
            {
                var resolvedValue = ((src == null) || false) ? null : src.IsEnabled;
                var propertyValue = mappingFunction.Invoke(resolvedValue, (dest == null) ? null : typeMapDestination.IsEnabled, ctxt);
                typeMapDestination.IsEnabled = propertyValue;
            }
            catch (Exception ex)
            {
                throw new AutoMapperMappingException(
                    "Error mapping types.",
                    ex,
                    AutoMapper.TypePair,
                    TypeMap,
                    PropertyMap);
            };

            return typeMapDestination;
        };
}
5
  • Are you sure you didn't want to write this .ForMember(dst => dst.Value, opt => opt.MapFrom(src => src.IsEnabled)); instead of .ForMember(dst => dst.Value, opt => opt.MapFrom(src => src)); Commented May 3, 2018 at 7:50
  • @CodeNotFound src is a Nullable<bool> in this case. Commented May 3, 2018 at 8:06
  • Make IsEnabledProperty a struct then. Commented May 3, 2018 at 8:11
  • @LucianBargaoanu Using a struct works for the value, but the initialized IsRequired property has the wrong value (false). Even if I pass in an initialized destination object to the mapping function. Commented May 3, 2018 at 9:14
  • Yes, you have to be careful with structs and copies. About the type converter, you get a destination only when you pass one because someone wanted that some time ago :) Different people have different expectations. Commented May 3, 2018 at 15:16

1 Answer 1

1

I think you want to map the whole bool? instead of only the value dest.IsEnabled.Value. This way you can ask the boolean if it has a value with dest.HasValue and use the value with dest.Value.

You might want to add AllowNullableMapping to the Automapper configuration.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.