Click here to Skip to main content
Click here to Skip to main content

Tagged as

Go to top

5 AutoMapper tips and tricks

, 3 Sep 2014
Rate this:
Please Sign up or sign in to vote.
5 useful tips to help get the most from AutoMapper

AutoMapper is a productivity tool designed to help you write less repetitive code mapping code. AutoMapper maps objects to objects, using both convention and configuration.  AutoMapper is flexible enough that it can be overridden so that it will work with even the oldest legacy systems.  This post demonstrates what I have found to be 5 of the most useful, lesser known features.

Tip: I wrote unit tests to demonstrate each of the basic concepts.  If you would like to learn more about unit testing, please check out my post C# Writing Unit Tests with NUnit And Moq.

Demo project code

This is the basic structure of the code I will use throughout the tutorial;

public class Doctor
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}
 
public class HealthcareProfessional
{
    public string FullName { get; set; }
}
 
public class Person
{
    public string Title { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}
 
public class KitchenCutlery
{
    public int Knifes { get; set; }
    public int Forks { get; set; }
}
 
public class Kitchen
{
    public int KnifesAndForks { get; set; }
}
 
public class MyContext : DbContext
{
    public DbSet<Doctor> Doctors { get; set; }
}
 
public class DbInitializer : DropCreateDatabaseAlways<MyContext>
{
    protected override void Seed(MyContext context)
    {
        context.Doctors.Add(new Doctor
        {
            FirstName = "Jon",
            LastName = "Preece",
            Title = "Mr"
        });
    }
}

I will refer back to this code in each example.

AutoMapper Projection

No doubt one of the best, and probably least used features of AutoMapper is projection.  AutoMapper, when used with an Object Relational Mapper (ORM) such as Entity Framework, can cast the source object to the destination type at database level. This may result in more efficient database queries.

No doubt one of the best, and probably least used features of AutoMapper is projection.  AutoMapper, when used with an Object Relational Mapper (ORM) such as Entity Framework, can cast the source object to the destination type at database level. This may result in more efficient database queries.

AutoMapper provides the Project extension method, which extends the IQueryable interface for this task.  This means that the source object does not have to be fully retrieved before mapping can take place.

Take the following unit test;

[Test]
public void Doctor_ProjectToPerson_PersonFirstNameIsNotNull()
{
    //Arrange
    Mapper.CreateMap<doctor, person="">()
            .ForMember(dest => dest.LastName, opt => opt.Ignore());
 
    //Act
    Person result;
    using (MyContext context = new MyContext())
    {
        context.Database.Log += s => Debug.WriteLine(s);
        result = context.Doctors.Project().To<person>().FirstOrDefault();
    }
 
    //Assert
    Assert.IsNotNull(result.FirstName);
}

The query that is created and executed against the database is as follows;

SELECT TOP (1) 
    [c].[Id] AS [Id], 
    [c].[FirstName] AS [FirstName]
    FROM [dbo].[Doctors] AS [c]

Notice that LastName is not returned from the database?  This is quite a simple example, but the potential performance gains are obvious when working with more complex objects.

Configuration Validation

Hands down the most useful, time saving feature of AutoMapper is Configuration Validation.  Basically after you set up your maps, you can callMapper.AssertConfigurationIsValid() to ensure that the maps you have defined make sense.  This saves you the hassle of having to run your project, navigate to the appropriate page, click button A/B/C and so on to test that you mapping code actually works.

Take the following unit test;

[Test]
public void Doctor_MapsToHealthcareProfessional_ConfigurationIsValid()
{
    //Arrange
    Mapper.CreateMap<doctor, healthcareprofessional="">();
 
    //Act
 
    //Assert
    Mapper.AssertConfigurationIsValid();
}

AutoMapper throws the following exception;

Quote:

AutoMapper.AutoMapperConfigurationException : Unmapped members were found. Review the types and members below. Add a custom mapping expression, ignore, add a custom resolver, or modify the source/destination type =================================================================== Doctor -> HealthcareProfessional (Destination member list) MakingLifeEasier.Doctor -> MakingLifeEasier.HealthcareProfessional(Destination member list) ------------------------------------------------------------------- FullName

AutoMapper can’t infer a map between Doctor and HealthcareProfessional because they are structurally very different.  A custom converter, or ForMemberneeds to be used to indicate the relationship;

[Test]
public void Doctor_MapsToHealthcareProfessional_ConfigurationIsValid()
{
    //Arrange
    Mapper.CreateMap<doctor, healthcareprofessional="">()
          .ForMember(dest => dest.FullName, opt => opt.MapFrom(src => string.Join(" ", src.Title, src.FirstName, src.LastName)));
 
    //Act
 
    //Assert
    Mapper.AssertConfigurationIsValid();
}

The test now passes because every public property now has a valid mapping.

Custom Conversion

Sometimes when the source and destination objects are too different to be mapped using convention, and simply too big to write elegant inline mapping code (ForMember) for each individual member, it can make sense to do the mapping yourself.  AutoMapper makes this easy by providing the ITypeConverter<TSource, TDestination> interface.

The following is an implementation for mapping Doctor to a HealthcareProfessional;

public class HealthcareProfessionalTypeConverter : ITypeConverter<doctor, healthcareprofessional="">
{
    public HealthcareProfessional Convert(ResolutionContext context)
    {
        if (context == null || context.IsSourceValueNull)
            return null;
 
        Doctor source = (Doctor)context.SourceValue;
 
        return new HealthcareProfessional
        {
            FullName = string.Join(" ", new[] { source.Title, source.FirstName, source.LastName })
        };
    }
}

You instruct AutoMapper to use your converter by using the ConvertUsing method, passing the type of your converter, as shown below;

[Test]
public void Legacy_SourceMappedToDestination_DestinationNotNull()
{
    //Arrange
    Mapper.CreateMap<doctor, healthcareprofessional="">()
            .ConvertUsing<healthcareprofessionaltypeconverter>();
 
    Doctor source = new Doctor
    {
        Title = "Mr",
        FirstName = "Jon",
        LastName = "Preece",
    };
 
    Mapper.AssertConfigurationIsValid();
 
    //Act
    HealthcareProfessional result = Mapper.Map<healthcareprofessional>(source);
 
    //Assert
    Assert.IsNotNull(result);
}

AutoMapper simply hands over the source object (Doctor) to you, and you return a new instance of the destination object (HealthcareProfessional), with the populated properties.  I like this approach because it means I can keep all my monkey mapping code in one single place.

Value Resolvers

Value resolves allow for correct mapping of value types.  The source object KitchenCutlery contains a precise breakdown of the number of knifes and forks in the kitchen, whereas the destination object Kitchen only cares about the sum total of both.  AutoMapper won’t be able to create a convention based mapping here for us, so we use a Value (type) Resolver;

public class KitchenResolver : ValueResolver<kitchencutlery, int="">
{
    protected override int ResolveCore(KitchenCutlery source)
    {
        return source.Knifes + source.Forks;
    }
}

The value resolver, similar to the type converter, takes care of the mapping and returns a result, but notice that it is specific to the individual property, and not the full object.

The following code snippet shows how to use a Value Resolver;

[Test]
public void Kitchen_KnifesKitchen_ConfigurationIsValid()
{
    //Arrange
 
    Mapper.CreateMap<kitchencutlery, kitchen="">()
            .ForMember(dest => dest.KnifesAndForks, opt => opt.ResolveUsing<kitchenresolver>());
 
    //Act
 
    //Assert
    Mapper.AssertConfigurationIsValid();
}

Null Substitution

Think default values.  In the event that you want to give a destination object a default value when the source value is null, you can use AutoMapper’s NullSubstitute feature.

Example usage of the NullSubstitute method, applied individually to each property;

[Test]
public void Doctor_TitleIsNull_DefaultTitleIsUsed()
{
    //Arrange
    Doctor source = new Doctor
    {
        FirstName = "Jon",
        LastName = "Preece"
    };
 
    Mapper.CreateMap<doctor, person="">()
            .ForMember(dest => dest.Title, opt => opt.NullSubstitute("Dr"));
 
    //Act
    Person result = Mapper.Map<person>(source);
 
    //Assert
    Assert.AreSame(result.Title, "Dr");
}

Summary

AutoMapper is a productivity tool designed to help you write less repetitive code mapping code.  You don’t have to rewrite your existing code or write code in a particular style to use AutoMapper, as AutoMapper is flexible enough to be configured to work with even the oldest legacy code.  Most developers aren’t using AutoMapper to its full potential, rarely straying away from Mapper.Map.  There are a multitude of useful tidbits, including; Projection, Configuration Validation, Custom Conversion, Value Resolvers and Null Substitution, which can help simplify complex logic when used correctly.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

jpreecedev
Software Developer Parasol
United Kingdom United Kingdom
An experienced Software Developer with a proven record of developing Enterprise N-tier architecture applications, using a variety of Microsoft technologies. Self-motivated person driven by the desire to be successful as part of a team and as an individual. Very passionate about personal development and keeping up-to-date with trends in our industry.
 
I enjoy all aspects of the Software Development life-cycle, and take great pride in passing my knowledge/skills on to other people to help make them more productive. I am constantly exploring new technologies so that I can improve my code and make it as robust and as scalable as possible.
 
My personal blog can be found at www.jpreecedev.com
Follow on   Twitter   Google+   LinkedIn

Comments and Discussions

 
GeneralMy vote of 5 PinmemberMahsa Hassankashi17-Sep-14 3:05 
Questionnice Pinmvp Akhil Mittal 4-Sep-14 2:35 
AnswerRe: nice Pinprofessionaljpreecedev4-Sep-14 6:43 
QuestionStatic mapping PinmemberAnjdreas4-Sep-14 2:21 
AnswerRe: Static mapping Pinprofessionaljpreecedev4-Sep-14 6:48 
GeneralRe: Static mapping PinmemberAnjdreas4-Sep-14 8:03 
QuestionGood tips PinmvpSacha Barber3-Sep-14 21:44 
AnswerRe: Good tips Pinprofessionaljpreecedev4-Sep-14 6:43 
GeneralMy vote of 5 PinmemberHumayun Kabir Mamun3-Sep-14 21:10 
GeneralRe: My vote of 5 Pinprofessionaljpreecedev4-Sep-14 6:42 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140916.1 | Last Updated 3 Sep 2014
Article Copyright 2014 by jpreecedev
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid