Getting Started with AutoMapper

If you are an experienced software developer you have no doubt written thousands of lines of code mapping properties of one object to properties of another object. This is commonly done when creating ViewModel objects or data transfer objects (DTOs). AutoMapper gives you a reusable, elegant solution to this problem. In this post, we will explore how AutoMapper does this.

What is AutoMapper?

AutoMapper is an object to object property mapping library. It is used to populate properties of one type from properties of another type. AutoMapper and other libraries like it promise to remove the need for developers to create that boring translation code we have all wasted time writing. However, the auto in AutoMapper works for a small handful of scenarios. If you follow the same convention they have followed, the configurations are definitely minimal. However, more complex scenarios require more complex configurations. Let’s take a look at a few examples.

The Scenario

Assume we are trying to populate a ConactViewModel object from a Contact object. That Contact object contains a Company endpoint named Company.

Listing 1.1 – The ContactViewModel Object

public class ContactViewModel
{
    private string _fullName = string.Empty;

    public int ContactID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public string FullName
    {
        get
        {
            if(string.IsNullOrEmpty(_fullName))
            {
                _fullName = $"{LastName}, {FirstName}";
            }

            return _fullName;
        }
        set
        {
            _fullName = value;
        }
    }

    public int CompanyID { get; set; }
    public string CompanyName { get; set; }
    public string JobTitle { get; set; }
    public DateTime BirthDate { get; set; }
}

The ContactViewModel object is a basic object that contains fields from both the Contact and Company.

Listing 1.2 – The Contact Object

public class Contact
{
    public int ID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int CompanyID { get; set; }
    public string JobTitle { get; set; }
    public DateTime BirthDate { get; set; }
    public Company Company { get; set; }
}

Listing 1.3 – The Company Object

public class Company
{
    public int ID { get; set; }
    public string Name { get; set; }
    public string Website { get; set; }
}

Property Mapping the Manual Way

Property mapping the manual way includes a lot of code and it is a terrible waste of time. I can’t count the number of times I have written code like this and you probably can’t either. It is a terrible, terrible way to spend your adult life.

Listing 1.4 – Manual Property Mapping

public List<ContactViewModel> GetContactViewModelsManual()
{
    List<Contact> source = GetContacts();

    List<ContactViewModel> destination = (from s in source
                                            select new ContactViewModel
                                            {
                                                BirthDate = s.BirthDate,
                                                CompanyID = s.CompanyID,
                                                CompanyName = s.Company.Name,
                                                ContactID = s.ID,
                                                FirstName = s.FirstName,
                                                JobTitle = s.JobTitle,
                                                LastName = s.LastName
                                            }).ToList();

    return destination;
}

This is a pretty basic example of standard mapping code using a LINQ query. The Company endpoint is only referred to one time and the Contact ID property is the only other mapping that is not straight across.

Property Mapping with AutoMapper

AutoMapper does a lot of heavy lifting for you. However, it only works for mappings that follow a standard convention.

Listing 1.5 – Simple AutoMapper Scenario

public List<ContactViewModel> GetContactViewModelsSimple()
{
    var config = new MapperConfiguration(cfg =>
    {
        cfg.CreateMap<Contact, ContactViewModel>();
    });

    IMapper mapper = config.CreateMapper();

    List<Contact> source = GetContacts();
    List<ContactViewModel> destination = new List<ContactViewModel>();

    foreach (Contact contact in source)
    {
        destination.Add(mapper.Map<Contact, ContactViewModel>(contact));
    }

    return destination;
}

We get the benefit of two of those conventions based on naming guidelines. The first, one-to-one mappings where the property name of object B matches the property name of object A such as ContactViewModel.FirstName equals Contact.FirstName. The second, where the property of object B matches the endpoint property name plus the actual property name. In this case, ContactViewModel.CompanyName is automatically mapped from Contact.Company.Name. So, in the case of the above code, the only property that is not mapped is the ContactID property.

Listing 1.6 – The ContactID Mapping

public List<ContactViewModel> GetContactViewModelsComplex()
{
    var config = new MapperConfiguration(cfg =>
    {
        cfg.CreateMap<Contact, ContactViewModel>()
        .ForMember(dest => dest.ContactID, map => map.MapFrom(src => src.ID));
    });

    IMapper mapper = config.CreateMapper();

    List<Contact> source = GetContacts();
    List<ContactViewModel> destination = new List<ContactViewModel>();

    foreach (Contact contact in source)
    {
        destination.Add(mapper.Map<Contact, ContactViewModel>(contact));
    }

    return destination;
}

In this scenario, I am creating a manual mapping for the ContactID property from the Contact.ID property using the ForMember() method. I have to admit, though, I found this code very cryptic to write. It felt like a foreign language and it didn’t feel right. It is a .NET Framework library and it should feel like I am writing .NET code but it did not. Surely one will get used to it but it felt really unnatural. Perhaps unnatural isn’t the right word. It felt too complex.

Listing 1.7 – An Alternate FullName Mapping

public List<ContactViewModel> GetContactViewModelsComplex()
{
    var config = new MapperConfiguration(cfg =>
    {
        cfg.CreateMap<Contact, ContactViewModel>()
        .ForMember(dest => dest.ContactID, map => map.MapFrom(src => src.ID))
        .ForMember(dest => dest.FullName, map => map.MapFrom(src => $"{src.FirstName} {src.LastName}"));
    });

    IMapper mapper = config.CreateMapper();

    List<Contact> source = GetContacts();
    List<ContactViewModel> destination = new List<ContactViewModel>();

    foreach (Contact contact in source)
    {
        destination.Add(mapper.Map<Contact, ContactViewModel>(contact));
    }

    return destination;
}

In this scenario, I am manually populating the FullName property using string interpolation just as an example. It follows the previous ForMember usage pretty closely and it highlights how to do string manipulation in a mapping.

Conclusion

In this post, I have given a few fairly simple examples of how to use AutoMapper in your projects. I do recommend, however, that you create the mappings in static classes that are available from static methods that you can then call upon from other classes so you don’t have to recreate the config and mappings each time.

I have very little doubt that AutoMapper could save you massive amounts of time if you are doing translations that follow the standard conventions. However, allowing an external library to influence the design of your models is lunacy. Models should be pure and not be affected by such things. So, it comes down to whether or not creating the mapping configuration or the mapping code is more onerous to you.

Leave a Reply

Your email address will not be published.