1

My question is similar to this question, only that I need to merge deeper levels.

I have two similar objects of the same type. I need to merge these together into a final result. One object is populated with data from a more reliable source, while the other object is more complete. My final result should contain all values from the more reliable source. But when a value is not present in the reliable source it should be filled from the more complete source.

My objects look like this (simplified):

public class Body {
    int Id
    Client Client
    Details Details
}

public class Client {
    int Id
    Name Name
    Address Address
}

public Name {
    string FirstName
    string LastName
    etc...
}

public class Address {
    string StreetName
    string HouseNumber
    etc...
}

public class Details {
    Details1 Details1
    Details2 Details2
    many more classes which contains more nested classes...
}

So, I found that I can use the AutoMapper Condition option to specify that only null values in the destination class should be mapped.

Mapper.CreateMap<Body, Body>()
            .ForAllMembers(opt => opt.Condition(dest => dest.DestinationValue == null));
Mapper.Map(completeObject, reliableObject);

But ForAllMembers only applies to the direct child members of that class. So the above statement will only check if the Id, Client en Details fields in reliableObject are null. It will not check values on deeper levels in the schema, for instance reliableObject.Client.Address.StreetName.

To achieve this I'd have to configure the Condition option for every class in my schema, which are a lot, and map them separately.

Is there a way to tell AutoMapper to apply this Condition to every level of the schema? Is AutoMapper even the best way to merge these objects?

I'm using AutoMapper version 3.2.1.

Community
  • 1
  • 1
Houkes
  • 54
  • 8

1 Answers1

1

In newer versions of AutoMapper there is Profile.AddConditionalMapper() which allows you to define global conventions, but in version 3 this is not possible.

What you could do however is make a generic method such as this:

public void MapIfDestinationIsNull<T>()
{
    Mapper.CreateMap<T, T>().ForAllMembers(opt => opt.Condition(dest => dest.DestinationValue == null));
}

And call that with all your types. Equally many lines of code, but each line is shorter.

Or if you have a way of getting all your types into an array (e.g. you have a separate assembly with all of them, or they have some kind of common interface/base class) you could use reflection to invoke this method for all your types:

foreach (var type in types)
{
    GetType().GetMethod("MapIfDestinationIsNull").MakeGenericMethod(type).Invoke(this, new object[] { });
}

The best advice I can give you however is to simply upgrade your AutoMapper, but if that's not possible then you'll have to resort to a bit of reflection. You could also peek in AutoMapper's source code and see how the newer versions do some of this stuff, and simply mimic that.

Fred Kleuver
  • 7,797
  • 2
  • 27
  • 38
  • Thanks Fred! I can't upgrade Automapper (large company blabla), but I will certainly try your suggestion for a generic method. – Houkes Mar 28 '18 at 15:29