27

I'm trying to map 2 objects of the same type. What I want to do is AutoMapper to igonore all the properties, that have Null value in the source object, and keep the existing value in the destination object.

I've tried using this in my "Repository", but it doesn't seem to work.

Mapper.CreateMap<TEntity, TEntity>().ForAllMembers(p => p.Condition(c => !c.IsSourceValueNull));

What might be the problem ?

Marty
  • 3,485
  • 8
  • 38
  • 69
  • Hi, can we configure the condition for all classes? I tried `Mapper.CreateMap` but it doesn't work (The type or namespace name 'TEntity' could not be found). Thanks. – stenlytw Sep 16 '15 at 17:03
  • Possible duplicate of [Skip null values with custom resolver](https://stackoverflow.com/questions/20021633/skip-null-values-with-custom-resolver) – Michael Freidgeim Apr 10 '18 at 13:26

6 Answers6

31

Interesting, but your original attempt should be the way to go. Below test is green:

using AutoMapper;
using NUnit.Framework;

namespace Tests.UI
{
    [TestFixture]
    class AutomapperTests
    {

      public class Person
        {
            public string FirstName { get; set; }
            public string LastName { get; set; }
            public int? Foo { get; set; }
        }

        [Test]
        public void TestNullIgnore()
        {
            Mapper.CreateMap<Person, Person>()
                    .ForAllMembers(opt => opt.Condition(srs => !srs.IsSourceValueNull));

            var sourcePerson = new Person
            {
                FirstName = "Bill",
                LastName = "Gates",
                Foo = null
            };
            var destinationPerson = new Person
            {
                FirstName = "",
                LastName = "",
                Foo = 1
            };
            Mapper.Map(sourcePerson, destinationPerson);

            Assert.That(destinationPerson,Is.Not.Null);
            Assert.That(destinationPerson.Foo,Is.EqualTo(1));
        }
    }
}
Void Ray
  • 9,849
  • 4
  • 33
  • 53
  • Hi, can we configure the condition for all classes? I tried `Mapper.CreateMap` but it doesn't work (The type or namespace name 'TEntity' could not be found). Thanks. – stenlytw Sep 16 '15 at 16:59
  • 2
    For later versions, the answer be @nenad is the right way. You can also check this answer: https://stackoverflow.com/a/43947731/2929906 – elfico Dec 20 '19 at 07:22
14

Condition overload with 3 parameters let's you make expression equivalent to your example p.Condition(c => !c.IsSourceValueNull):

Method signature:

void Condition(Func<TSource, TDestination, TMember, bool> condition

Equivalent expression:

CreateMap<TSource, TDestination>.ForAllMembers(
    opt => opt.Condition((src, dest, sourceMember) => sourceMember != null));
Nenad
  • 24,809
  • 11
  • 75
  • 93
1

So far I've solved it like this.

foreach (var propertyName in entity.GetType().GetProperties().Where(p=>!p.PropertyType.IsGenericType).Select(p=>p.Name))
   {
      var value = entity.GetType().GetProperty(propertyName).GetValue(entity, null);
      if (value != null)
      oldEntry.GetType().GetProperty(propertyName).SetValue(oldEntry, value, null);
    }

but still hoping to find a solution using AutoMapper

Marty
  • 3,485
  • 8
  • 38
  • 69
0

Taking Marty's solution (which works) one step further, here is a generic method to make it easier to use:

public static U MapValidValues<U, T>(T source, U destination)
{
    // Go through all fields of source, if a value is not null, overwrite value on destination field.
    foreach (var propertyName in source.GetType().GetProperties().Where(p => !p.PropertyType.IsGenericType).Select(p => p.Name))
    {
        var value = source.GetType().GetProperty(propertyName).GetValue(source, null);
        if (value != null && (value.GetType() != typeof(DateTime) || (value.GetType() == typeof(DateTime) && (DateTime)value != DateTime.MinValue)))
        {
            destination.GetType().GetProperty(propertyName).SetValue(destination, value, null);
        }
    }

    return destination;
}

Usage:

class Person
{
    public string Name { get; set; } 
    public int? Age { get; set; } 
    public string Role { get; set; } 
}

Person person = new Person() { Name = "John", Age = 21, Role = "Employee" };
Person updatePerson = new Person() { Role = "Manager" };

person = MapValidValues(updatePerson, person);
primaryobjects
  • 35
  • 1
  • 2
  • 9
0

Work Around - Add DataMember property in destination type [DataMember(EmitDefaultValue = false)] will remove the property if the source value is null.

[DataContract]
class Person
{
    [DataMember]
    public string Name { get; set; } 

    [DataMember]
    public int? Age { get; set; } 

    [DataMember(EmitDefaultValue = false)]
    public string Role { get; set; } 
}

if the Role value is null Role property will be removed.

0

I used this code and it's worked

.ForAllMembers(opt => opt.Condition((src, dest, srcMember) => srcMember != null));