Automapper: Issue with mapping Xml to types that share a common parent

image

Let’s say you have a simple object graph consisting of a common base class called bicycle which has a property called Id.
Two specialized bicycle classes exist; tandem bike and mountain bike

Now, given the xml below:

image

We want to use automapper to convert from the xml to actual C# classes.

 

Let’ s start off with a simple test

        [Fact]
        public void CanMapFromXElementToBicycle()
        {
            var arrange = BicycleTestDataXml.Element("Bicycle");

            var act = arrange.Map();

            act.Id
                .Should().Equal("001");
        }

Given the first bicycle element, when I map that to a bicycle class using automapper, the id of that bicycle should be equal to 001.

This is the code to make that test pass:

    static class XElementExtensions
    {
        static XElementExtensions()
        {
            Mapper.CreateMap()
                .ForMember(
                    to => to.Id,
                    m => m.ResolveUsing()
                        .FromMember(from => from.Attribute("id")));
        }

        public static TDestination Map(this XElement instance)
        {
            return Mapper.Map(instance);
        }
    }

It’s an extension method on the XElement class that uses automapper to convert the xml to an instance of the bicycle.

And…is it green?

image

 

Update our tests to include mountain bikes

        [Fact]
        public void CanMapFromXElementToMountainBike()
        {
            var arrange = BicycleTestDataXml
                .Descendants("Bicycle")
                .Where(x => x.Attribute("type").Value == "1")
                .Last();

            var act = arrange.Map();

            act.Id
                .Should().Equal("002");

        }

Given the last mountain bike element (type == 1), when I map that to a mountain bike class using automapper, the id of that bicycle should be equal to 002.

To make that code pass I have to update automapper to include mountain bikes.

image

Since I want to reuse the same logic as for regular bikes I’m just hooking into the bicycle mapper (include) and finally adding a mapper for the mountain bike so automapper will know about it.

Still green?

image

 

Even more complicated…how about the tandem bike then

        [Fact]
        public void CanMapFromXElementToTandemBike()
        {
            var arrange = BicycleTestDataXml
                .Descendants("Bicycle")
                .Where(x => x.Attribute("type").Value == "2")
                .First();

            var act = arrange.Map();

            act.Id
                .Should().Equal("003");
        }

Update the mapper to include the tandem bike.

image

Still green?

image

NO!!

So it seems that the tandem bike test passes but I broke my previous test for the mountain bike.

 

Why

Well, the reason probably is that to automapper including mappings for mountain bike and tandem bike from XElement are the of the same importance since these two classes both inherit from bicycle.
And since tandem bike was added “last” (included last) it will “overwrite” the include for mountain bike and that mapping will fail for the Id parameter which then ends up as null.

 

Solution

        static XElementExtensions()
        {
            Mapper.CreateMap()
                .AddCommonPropertiesFor();

            Mapper.CreateMap()
                .AddCommonPropertiesFor();

            Mapper.CreateMap()
                .AddCommonPropertiesFor();
        }

        static IMappingExpression AddCommonPropertiesFor
            (this IMappingExpression instance)
                where TBicycle : Bicycle
        {
            return instance
                .ForMember(
                    to => to.Id,
                    m => m.ResolveUsing()
                        .FromMember(from => from.Attribute("id")));

        }

One solution that works (maybe there’s a better way I haven’t seen) is to extract the common properties that are in the base type to a separate extension method that all the CreateMap calls will use.

Green again??

image

,

  1. Leave a comment

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

%d bloggers like this: