Using AutoMapper with Strongly-Typed DataRows

Over the years I’ve done a lot of work involving moving data in and out of databases. In most cases, this involves taking data from some form of data access object (DAO) and then copying it to a simple data transfer object (DTO) and vice-versa. I do this when working on web services or data synchronization utilities, and the practice can be tedious and repetitive. And, as with anything tedious and repetitive, it can introduce hard-to-spot bugs.

I was recently delighted to discover a very nice open source project called AutoMapper which handles these sorts of scenarios for you in a very simple manner. With only a couple of lines of code using static methods AutoMapper will copy the properties of a source object to a destination object (of different types) using very straight forward name mapping. AutoMapper also allows you to customize how each member of the source object is mapped (called projection), so you can do any necessary transformation in a very terse and easy-to-read manner.

That said, the first scenario where I wanted to make use of AutoMapper had me initially scratching my head: I had two strongly-typed datarows from different, but similar, databases and I wanted to copy the property values of one datarow to the other. I also wanted to make use of the .ForMember() and .MapFrom() methods to transform a few properties. While the mapping succeeded without error, the properties of my source row were copied to my destination row exactly, disregarding any custom mapping I provided.

Here is an example:

            DestinationData.PersonDataTable destinationDataTable = new DestinationData.PersonDataTable();
            DestinationData.PersonRow destinationRow = destinationDataTable.NewPersonRow();

            AutoMapper.Mapper.CreateMap<SourceData.PersonRow, DestinationData.PersonRow>()
                //with this ForMember, every FirstName should be "First!"
                .ForMember(src => src.FirstName, opt => opt.MapFrom(src => "First!"));

            AutoMapper.Mapper.Map<SourceData.PersonRow, DestinationData.PersonRow>(sourceRow, destinationRow);

            Debug.Assert(destinationRow.FirstName.Equals("First!"), "datarow mapping failed");

After running into the issue, I created a sample illustrating the issue, hosted it on Github, reported the issue, and took a break from the issue.

Taking a break from an issue is almost always a great help for me. Once I started looking into the issue again, it was only a few minutes and logical steps before I realized what was going on. Being new to AutoMapper, I had made an assumption that was totally invalid: I assumed AutoMapper was only mapping my type’s properties, not ancestor properties. Once I started thinking along these lines the solution came quickly.

The ancestor DataRow class has an ItemArray public property. It’s an array of objects that provides direct access to the row’s values. AutoMapper was doing its job and doing it well. After copying over the individual, strongly-typed properties found in my DataRow implementations it was also copying the ItemArray property, which is why I always ended up with an exact copy of the source row without any custom mapping via .ForMember() and .MapFrom()!

The working code looks like this:

            DestinationData.PersonDataTable destinationDataTable = new DestinationData.PersonDataTable();
            DestinationData.PersonRow destinationRow = destinationDataTable.NewPersonRow();

            AutoMapper.Mapper.CreateMap<SourceData.PersonRow, DestinationData.PersonRow>()
                //with this ForMember, every FirstName should be "First!"
                .ForMember(src => src.FirstName, opt => opt.MapFrom(src => "First!"))

                //!!don't map property ItemArray from DataRow!!
                .ForMember(src => src.ItemArray, opt => opt.Ignore());

            AutoMapper.Mapper.Map<SourceData.PersonRow, DestinationData.PersonRow>(sourceRow, destinationRow);

            //!!this assertion will fail unless you use opt.Ignore() for ItemArray!!
            Debug.Assert(destinationRow.FirstName.Equals("First!"), "datarow mapping failed");

With the extra .ForMember() in place the issue with mapping goes away. I’m excited to be back on track with AutoMapper as it seems like a great tool for mapping objects to objects.

You can view the example project on Github here.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s