Monthly Archives: April 2011

AutoMapper is awesome, but ArgumentNullException on IEnumerable.Select pisses me off.

Let me start this off by saying:
I am way undereducated with AutoMapper. I’m fairly certain there is a more elegant way to accomplish this, but I did it my way for the following reasons:

1) I’m stubborn
2) I don’t want to create ValueResolver classes. I don’t know why exactly, but I prefer to just have maps.
3) I can’t rename any of my classes, nor can I change the structure. I’m stuck with what I have.
4) In my instance, I can’t create a map directly from the source type to the destination type – I need to keep the actual references.

The situation:
I have a large number of classes with one to many relationships. For example, let’s take three simple classes:

public class Person
{
     public IList Addresses { get; set; }
}

public class PersonAddress
{
     public Person Person { get; set; }
     public PhysicalAddress PhysicalAddress { get; set; }
}

public class PhysicalAddress
{
     public string City { get; set; }
}

public class DestinationList
{
     public IList<PhysicalAddress> Cities { get; set; }
}

So, I tried a few different methods, with varying results.

Attempt 1:

public static void CreatePersonAddressMap()
{
     AutoMapper.Mapper.CreateMap()
          .ForMember(dest => dest.Cities,
               config => config.MapFrom(person => person.Addresses.Select(add => add.PhysicalAddress)));
          //Throws null reference exception if person.Addresses is null! OH NOES!
}

Result: ArgumentNullException when person.Addresses is null.

Attempt 2:

public static void CreatePersonAddressMap()
{
     AutoMapper.Mapper.CreateMap()
          .AfterMap((person, dest) =>
          {
               if (person.Addresses != null)
                    dest.Cities = person.Addresses.Select(add => add.PhysicalAddress).ToList();
          });
          //This method works, but it's A) Ugly and B) fails the AssertConfigurationIsValid check.
          //I could add .ForMember(dest => dest.Cities, s=> s.Ignore()), but again that sucks
}

Result: Works for the actual mapping, even when person.Addresses is null. However, fails the AutoMapper.Mapper.AssertConfigurationIsValid check

Attempt 3: I thought for sure this would work; what else could the .Condition method be for?

public static void CreatePersonAddressMap()
{
     AutoMapper.Mapper.CreateMap()
          .ForMember(dest => dest.Cities,
               config =>
               {
                    config.Condition(person => person.Addresses != null);
                    config.MapFrom(
                    person => person.Addresses.Select(add => add.PhysicalAddress));
               });
               //This is the method that I thought would work, but the Condition feature is underdocumented, at
               //least in my undereducated opinion.
}

Result: ArgumentNullException when person.Addresses is null. Grr.

Attempt 4:

public static void CreatePersonAddressMap()
{
     AutoMapper.Mapper.CreateMap()
          .ForMember(dest => dest.Cities,
          config => config.MapFrom(
               person =>
                    person.Addresses == null ? null : person.Addresses.Select(add => add.PhysicalAddress)));
               //This is the method I went with. It's still ugly, but it's (i think) a bit easier to follow, and won't crash
               //the AssertConfigurationIsValid check.
}

Result: It works, doesn’t throw an exception, and correctly projects the desired information. However, I will readily admit that it is quite ugly and would much prefer a better option for the configuration of AutoMapper…. Or some advice on what I’m doing wrong. That works too.