3

I have the following sample objects..

  public class ComplexObject
  {
    public string Name { get; set; }
    public SimpleObject Child1 { get; set; }
    public SimpleObject Child2 { get; set; }
  }
  public class SimpleObject : IEquatable< SimpleObject >
  {
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Gender { get; set; }
    public int? Age { get; set; }
  }

with the following AutoMapper configuration

  Mapper.CreateMap<SimpleObject, SimpleObject>()
    .ForAllMembers(expression=>expression.Condition(r=>!r.IsSourceValueNull));

  Mapper.CreateMap<ComplexObject, ComplexObject>()
    .ForAllMembers(expression=>expression.Condition(resolutionContext=>!resolutionContext.IsSourceValueNull));

and the following NUnit test...

[SetUp] public void Should_run_before_each_test()
{
  child1 = new SimpleObject { FirstName = "Tom", LastName = "Smith", Age = 34, Gender = "Male" };
  child2 = new SimpleObject { FirstName = "Andy", LastName = "Smith-bob", Age = 21, Gender = "Male" };
}

[ Test ]
public void Should_ignore_null_properties_in_nested_objects()
{
  var source = new ComplexObject()
  {
    Name = "blue",
    Child1 = new SimpleObject{FirstName = "dot", LastName = "net"}
  };
  var destination = new ComplexObject()
  {
    Name = "Andy",
    Child1 = child1,
    Child2 = child2
  };

  destination = Mapper.Map(source, destination);

  destination.Name.Should(Be.EqualTo(source.Name));
  destination.Child1.FirstName.Should(Be.EqualTo("dot"));
  destination.Child1.LastName.Should(Be.EqualTo("net")  );
  destination.Child1.Age.Should(Be.EqualTo(child1.Age)  );
  destination.Child1.Gender.Should(Be.EqualTo(child1.Gender)  );
}

The above test fails when asserting the age as AutoMapper is pushing null through to the destination object.

Am I expecting too much from AutoMapper, or have I missed some vital map configuration step.

The ultimate goal is to have a very complex domain object bound to incoming form data via an MVC action. AutoMapper will then be used to merge only non-null properties (at all depths of the object graph) into the real instance being maintained throughout a multi step form.

Just in case anyone needs to know... I have also tried the following mapping configuration without any luck :(

  Mapper.CreateMap<ComplexObject, ComplexObject>()
    .ForMember(x=>x.Child1, l=>l.ResolveUsing(x=>x.Child1 == null?null:Mapper.Map<SimpleObject,SimpleObject>(x.Child1)))
    .ForMember(x=>x.Child2, l=>l.ResolveUsing(x=>x.Child2 == null?null:Mapper.Map<SimpleObject,SimpleObject>(x.Child2)))
    .ForAllMembers(expression=>expression.Condition(resolutionContext=>!resolutionContext.IsSourceValueNull));
andycwk
  • 826
  • 2
  • 9
  • 19

1 Answers1

1

Why do you expect the value on the destination object is not null if the source value is null? The default value for int? is null, and therefore, when you call

destination.Child1.Age.Should(Be.EqualTo(child1.Age));

The .Should is what's failing. This should be expected behavior.

Try something like Assert.Null(detination.Child1.Age), and it should pass. AutoMapper is not 'pushing' the value through, there is no source value and therefore, Age is just going to have it's default value.

scottm
  • 27,829
  • 22
  • 107
  • 159
  • nope.. not when I'm trying to convince AutoMapper to ignore null source values as per the attempted mapping below.. `Mapper.CreateMap() .ForAllMembers(expression=>expression.Condition(r=>!r.IsSourceValueNull));` – andycwk Jul 20 '12 at 14:14
  • Can you add what you have assigned to `child1` and `child2`? – scottm Jul 20 '12 at 14:16
  • The problem is that in your example, it's not mapping `SimpleObject` to `SimpleObject`, it's mapping `ComplexObject` to `ComplexObject`. Because `source.Child1` is not null, it just assigns it to `destination.Child1`. – scottm Jul 20 '12 at 14:42
  • Yeh.. I too had got that far... I was under the presumption that AutoMapper would/should handler deeper type mappings... – andycwk Jul 20 '12 at 14:46
  • It's one of the reasons that I tried the more complex mapping with resolvers attached to each of the child objects... that still fails :( – andycwk Jul 20 '12 at 14:47
  • 2
    From this question: http://stackoverflow.com/questions/2463183/merge-two-objects-to-produce-third-using-automapper it looks like the ValueInjector project could do exactly what you need. – scottm Jul 20 '12 at 14:53