two way automapper mapping with child properties mapped too

Quite often in our development projects which use automapper we will not only want to convert an object A to an object B but also from B to A. Think of a scenario using ASP.Net Web API and MVC for example where the web API returns a customerDTO which is converted to a customerViewModel and used to display a strongly typed razor view. On post back of that view we are going to want to turn the viewModel (if it’s valid) back into a customerDTO for passing to the web API PUT /Customer method for updating.

Very limited automapper two way mapping support

Automapper has very basic support for two way mapping out of the box via the ReverseMap() method which will allow you to reverse a map in one line rather than two like below:

reverseMap

In the above scenario ReverseMap() makes things slightly cleaner but as the original A to B mapping is very simple we are really only saving a few characters. What however if A and B were not perfect mirrors of each other and we needed ForMember() overrides because the names from the two objects don’t map or we need to do anything other than simple assignment from source to destination? Unfortunately there isn’t anything that can help with this scenario as ReverseMap() doesn’t map these overrides so you need to manually do them. It would be nice for example if the first block below was equivalent to the second and third blocks combined as it would save a good bit of time.

automapper-not-eqiuvalent

Automapper 4.2 two way mapping support

Fortunately thanks to Automapper’s extension points and .net extension methods it’s not too difficult to rig up something a little more useful. I spent a couple of hours on the below extension method over the weekend.

mapping-create

Three important parts of the extension method (which is available to download below) which are highlighted above:

1. Reversing of the overall map and returning of IMappingExpression, just like built in ReverseMap() does.
2. Storing the old destination value (now the new source value) into correct new destination property for value types.
3. Setting reference types by casting from object (creating cast errors for lists at moment – to change)

I can then configure my original A->B mappings and call the new reverse map method in a manner similar to below:

configure-automapper

I’m getting the reverse map to correctly map back primitive properties which are of the same type whose names just don’t match. Instance properties with or without the same name between source and destination seem to be working too.

Note the highlights in yellow above, for mappings from reference to value types and for collections you still need explicit mapping. For example for the initial PlayerViewModel->PlayerDTO mapping I used ForMember to convert from int->string, but an exception was thrown when I tried to reverse it if AssertConfgurationIsValid() was being called. If I removed the AssertConfgurationIsValid() call the destination int was always 0. I think I will need to use a type convertor like the accepted answer on this stackoverflow page. At the moment if types are not both valueType or both referenceType I continue so don’t attempt the mapping.

Although collections with different names were mapping correctly and populating the destination property, the AssertConfigurationIsValid() function was throwing a non ‘missing property’ type exception as my code was causing a casting issue when going from object -> list which I’m looking into. Therefore I am just skipping the reverse rule for generic types for the moment and continuing to the next map. To recap… the reverse mapping for collections worked without an explicit ForMember map, but AssetConfigurationIsValid() failed. If you don’t use the AssetConfigurationIsValid() method you don’t need to explicitly set the reverse mapping for lists etc. (but remember to remove the IsGenericType skip clause I’ve got in the extension method first)

Download the full two way reverse map automapper code

As you can see it still needs a little bit of work… but definitely an improvement on what was there. Here is the full ReverseMapWithForMemberPreserved extension method code, including the viewModels, DTOs, mapping configurations etc. The viewModels and DTOs model football teams and players etc. and yes I am a Liverpool Football Club supporter.

You will see a version of the extension for both Automapper 4.2 and 4.1 and below. The new 4.2 version has dropped the static API so things needed to be done slightly different than before. Lots of things which were in 4.1 are now marked as obsolete in 4.2 which is the version I ran both the 4.1 and 4.2 approaches in. These obsolete methods will be deleted in 5.0 so it’s recommend to upgrade to 4.2+ as soon as you can.

Automapper thrives on convention over configuration

Hopefully you can use the above code as a start if you want to try and rig improved bi-directional mapping into automapper. In an ideal world it wouldn’t be needed however as I’d obviously recommend a convention over configuration approach where property names on source and destination classes match exactly or at least use some defined format which can possibly be mapped using ‘RecognizePrefix’. See ‘Recognizing pre/postfixes’ section on the automapper configuration wiki page on Github for an example of how this might work.

The more convention that’s used, the more value I feel is derived from automapper. If every property name differs between your source and destination classes then you’re going to have so many .ForMember() explicit mappings that it probably would make more sense to just use plain left right C# assignment to do the mapping.

Leave a Reply

Your email address will not be published. Required fields are marked *