Tuesday, May 22, 2012

Mapping a single property to a complex model

I ran into a situation recently where I wanted to flatten several different objects into a single model that would be delivered via JSON to the client code. The tool I'm using as the basis for this is AutoMapper.; I found a very useful post from Owain Wragg that I used as the basis for mapping the various object components onto the single model.

The basic idea is that you use the feature in AutoMapper that let’s you map an object onto an existing object rather than create a new one. First, you map one object onto the destination normally, creating the destination object, then apply mappings from each of the other objects onto the newly created destination object. Slick!

One issue that I had was creating the mappings from each of the sub-objects onto the destination. This model has lots of properties, most filled in from the first of the source objects, with each succeeding object filling in just one property on the model. Ignoring all of those properties for each of the source/destination maps was going to make my configuration long and painful to write. Fortunately, I recalled a trick you can use with AutoMapper to make this easy.

First you create a mapping that ignores all members on the destination. Then you create a map that maps just the single property that you are interested in. Voila! you now have a simple map that projects just a single property onto the destination. I created a little helper method for my AutoMapper configuration class to make things easier. I suspect it would be trivial to make it an extension method, but I’m just using it as a static method on my configuration class.

private static void MapSingleProperty<Source,Destination>( Expression<Func<Destination,object>> selector, Action<IMemberConfigurationExpression<Source>> mapping )
{
    Mapper.CreateMap<Source,Destination>()
          .ForAllMembers(opt => opt.Ignore());
    Mapper.CreateMap<Source,Destination>()
          .ForMember(selector,mapping);
}

Note: it’s important to do the ignore first so that the mapping for the single property replaces the ignore mapping rather than the other way around.