Tuesday, August 9, 2011

AutoMapper

AutoMapper is a «convention-based object-to-object mapper». According to the description, «AutoMapper uses a fluent configuration API to define an object-object mapping strategy. AutoMapper uses a convention-based matching algorithm to match up source to destination values. Currently, AutoMapper is geared towards model projection scenarios to flatten complex object models to DTOs and other simple objects, whose design is better suited for serialization, communication, messaging, or simply an anti-corruption layer between the domain and application layer.»

I’ve been using AutoMapper to map entity objects between data and service layers, as well as service and UI layers. What I mean with mapping is doing stuff such as:

public TDestination Map(TSource src) {

TDestination dest = new TDestination ();

dest.Field1 = src.Field1;

dest.Field2 = src.Field2 + src.Field3;

dest.Field3 = src.Field10;

// …

return dest;

}

This is boring and repetitive code, and it’s what AutoMapper wants to avoid writing.

In its simplest usage, we setup the mapper with:

using AutoMapper;

[…]

Mapper.CreateMap<TSource , TDestination>();

and then convert by calling:

TDestination dest = Mapper.Map<TSource , TDestination>(srcObject);

By default, AutoMapper only maps properties that have the same names on the source and destination, however you can parameterize the mapper in a different way. For example, assuming that Field10 in TSource becomes Field3 in TDestination, you can write:

Mapper.CreateMap<TSource , TDestination>()
    .ForMember( dst => dst.Field3, options => options.MapFrom(src => src.Id));

You can add as many ForMember clauses as you want. Also note that dst, options are src are not variable names, but are part of the lambda function definitions.

After the map is setup like this, the call to Map will now convert the objects correctly.

So AutoMapper is an extremely convenient way to map between objects, and more convenient the more similarity there is in the names of the properties of the objects being mapped.

 

However, and quite obviously, AutoMapper does this by using reflection, and I wanted to measure the impact of doing these conversions this way, compared with the hand-coded assignments shown at the top.

I created two examples: in the first, the two objects have exacly the same structure, so AutoMapper does all the work. In the second, the destination type has the same fields, but with different names, so I had to use ForMember once for each of the 6 fields in my test classes.

I then created a loop that converted using either of the two methods a number of times and printed out the elapsed time. Here are the results:

Type of Conversion Number of loop iterations Elapsed time using AutoMapper Elapsed time hand-coded map Ratio
Direct 100.000 2 sec, 165 ms 20 ms 1:108
With Property Renames 100.000 3 mins, 14 secs, 157ms 25 ms 1:7726

The first time was according to what I expected, but the second was actually much larger.

Getting these results, I then tried an optimization, which was to create the map outside of the test loop – but still after the timer start (assuming it could be created and stored in some in-memory cache), and re-ran the tests. This time the results where much better, especially in the renames case, showing that setting up the map with renames can have a large impact on the execution times. The following table shows the results.

Type of Conversion Number of loop iterations Elapsed time using AutoMapper Elapsed time hand-coded map Ratio
Direct 100.000 2 sec, 611 ms 24 ms 1:109
With Property Renames 100.000 2 sec, 508 ms 27 ms 1:93

Quite surprising that the renames option is now actually faster than the automatic direct conversion.

 

My conclusion: I’ll go on using AutoMapper for its convenience when writing code, but if performance is an issue, I’ll just directly hand-code the mapping.
Pre-creating and populating a cache of Mappers would also be a viable alternative, but hard to justify in terms or architecture.

Check out the codeplex site for more features of AutoMapper, such as Flattening or Projection, or contact me if you want the source code I used for these tests.