2

Is there a way to ignore mapping null values to destination globally (for all mapping configurations)?

Something that goes in here:

    //Automapper config
    Mapper.Initialize(cfg =>
    {
        //Initializes all mapping profiles in the assembly
        cfg.AddProfiles(Assembly.GetExecutingAssembly().GetName().Name);

        //If map is called without a profile creates default map
        cfg.CreateMissingTypeMaps = true;
    });

This is for instance mapping. Here is one example of what i'm trying to accomplish.

//Models
    class User {
      public string Name {get; set;}
      public string Email {get; set;}
      public string Password {get; set;}
    }

    class UserDto {
      public string Name {get; set;}
      public string Email {get; set;}
    }

//Start instances
     var user = new User { Name = "John", Email = "john@live.com", Password = "123"};

     var userDto = new UserDto { Name = "Tim" };
//Notice that we left the Email null on the DTO


//Mapping
    Mapper.Map(userDto, user);

The result ends up with the user having a null email. I want the user email to not be changed unless a new email is provided on the source (userDto). In other words ignore all null properties of all types on the source object from overwriting destination (User)

UPDATE: None of the answers below solve the problem. They simply do not work for collections. While the Autommaper figures out this bug I was able to get around my problem by using an ExpandoObject to filter out all null properties before mapping as below:

var patchInput = new ExpandoObject() as IDictionary<string, object>;

foreach (var property in userDto.GetType().GetProperties())
    if (property.GetValue(userDto) is var propertyValue && propertyValue != null)
        patchInput.Add(property.Name, propertyValue);

Mapper.Map(patchInput, user);
DonO
  • 1,030
  • 1
  • 13
  • 27

1 Answers1

1

It should work

Mapper.Initialize(cfg =>
        {
            cfg.AddProfiles(Assembly.GetExecutingAssembly().GetName().Name);
            cfg.CreateMissingTypeMaps = true;
            cfg.ForAllMaps((typeMap, map) =>
                map.ForAllMembers(option => option.Condition((source, destination, sourceMember) => sourceMember != null)));
        });
Valerii
  • 2,147
  • 2
  • 13
  • 27
  • This answers my question but it seems that the other conditions defined on individual mapping profiles no longer work. :\ – DonO Sep 26 '17 at 21:10
  • 1
    @DonO It works for Nulable numbers like int?, for value types like int it won't work because value cannot be null. – Valerii Sep 28 '17 at 16:10
  • @DonO For collections it is a defect in AutoMapper. For collections you can use my alternative solution. cfg.CreateMap().ConvertUsing(new IgnoringNullValuesTypeConverter()); – Valerii Sep 28 '17 at 16:13
  • @DonO Why unaccepted? It works for example you provided. – Valerii Sep 28 '17 at 16:50
  • @Valerri CreateMap().ConvertUsing(new IgnoringNullValuesTypeConverter()); wont work because it needs to be IEnumerable but the problem is the types of collections aren't the same. – DonO Sep 28 '17 at 19:04
  • @DonO. IEnumerable is inherited from IEnumerable. I tried List -> List, List -> ObservableCollection, ObservableCollection -> ObservableCollection. It works. – Valerii Sep 28 '17 at 19:13
  • What about something like IList to IList . Its a valid map given there is a profile for the objects these lists contain however now you have two different types. How would you handle that? – DonO Sep 28 '17 at 19:18
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/155547/discussion-between-dono-and-valerii). – DonO Sep 28 '17 at 19:22