Monday, December 24, 2012

C# Object-to-Object Mapping - AutoMapper Example

Imaging this scenario: We have an Employee object with many properties in it, and we want to display our Employee in a DataGrid or some other control. For this purpose we need a lighter version of Employee, let's call it EmployeeViewItem. A list of EmployeeViewItem will need to bind to our DataGrid.
public class Employee
{
    public string Name { get; set; }
    public string Email { get; set; }
    public Address Address { get; set; }
    public string Position { get; set; }
    public bool Gender { get; set; }
    public int Age { get; set; }            
    public int YearsInCompany { get; set; }
    public DateTime StartDate { get; set; }
}

public class Address
{
    public string Country { get; set; }
    public string City { get; set; }
    public string Street { get; set; }
    public int Number { get; set; }            
}

public class EmployeeViewItem
{
    public string Name { get; set; }
    public string Email { get; set; }
    public string Address { get; set; }
    public string Position { get; set; }
    public string Gender { get; set; }
    public int Age { get; set; }            
    public int YearsInCompany { get; set; }
    public string StartDate { get; set; }
}
Ok, so what we usually do? Something like this, right?
EmployeeViewItem viewItem = new EmployeeViewItem();
viewItem.Name = employee.Name;
viewItem.Email = employee.Email;
viewItem.Address = employee.Address.City + employee.Address.Street + employee.Address.Number;
viewItem.Position = employee.Position;
viewItem.Gender = employee.Gender == true ? "Man" : "Female";
viewItem.Age = employee.Age;
viewItem.YearsInCompany = employee.YearsInCompany;
viewItem.StartDate = employee.StartDate.ToShortDateString();
Quite annoying, isn't it? What if we could do this mapping automatically in some way? I hasten to please you - yes, you can map these two object automatically with AutoMapper! As written on AutoMapper webpage - "AutoMapper is a simple little library built to solve a deceptively complex problem - getting rid of code that mapped one object to another."

I'll show how to use AutoMapper on our Employee to EmployeeViewItem mapping example.
First, download AutoMapper from here. It's a standalone assembly, so you'll need to add reference to it in your project.

In order to tell to AutoMapper what exactly to map we need to write this line of code:
Mapper.CreateMap<Employee, EmployeeViewItem>();
And to actually perform the mapping we write this thing:
EmployeeViewItem employeeViewItem = Mapper.Map<Employee, Employee>(employee);
Where employee is an object of Employee class. This is it!
But, as you probably noticed, our EmployeeViewItem class has several inconsistencies with Employee class. Address is a complex object in Employee, but a simple string in EmployeeViewItem. Gender is boolean in Employee, but is a string in EmployeeViewItem. StartDate is a DateTime in Employee, but a string in EmployeeViewItem. So, we need to do some tweaks to CreateMap method in Mapper. The final code will look like this:

class Program
{
    static void Main(string[] args)
    {
        Mapper.CreateMap<Employee, EmployeeViewItem>()
           .ForMember(ev => ev.Address, 
                       m => m.MapFrom(a => a.Address.City + ", " + 
                                           a.Address.Street + " " + 
                                           a.Address.Number)
                      )
           .ForMember(ev => ev.Gender, 
                       m => m.ResolveUsing<GenderResolver>().FromMember(e => e.Gender))
           .ForMember(ev => ev.StartDate, m => m.AddFormatter<DateFormatter>());

        Employee employee = new Employee
        {
            Name = "John SMith",
            Email = "john@codearsenal.net",
            Address = new Address
            {
                Country = "USA",
                City = "New York",
                Street = "Wall Street",
                Number = 7
            },
            Position = "Manager",
            Gender = true,
            Age = 35,                
            YearsInCompany = 5,
            StartDate = new DateTime(2007, 11, 2)
        };
        
        EmployeeViewItem employeeVIewItem = Mapper.Map<Employee, EmployeeViewItem>(employee);            
    }        

    public class GenderResolver : ValueResolver<bool, string>
    {
        protected override string ResolveCore(bool source)
        {
            return source ? "Man" : "Female";
        }
    }

    public class DateFormatter : IValueFormatter
    {
        public string FormatValue(ResolutionContext context)
        {
            return ((DateTime)context.SourceValue).ToShortDateString();
        }
    }
}
Voila! Employee object successfully mapped to EmployeeViewItem:
AutoMapper example results

Download the source code (Visual Studio 2012 project).

9 comments:

  1. Small error in the article :

    And to actually perform the mapping we write this thing:
    EmployeeViewItem employeeViewItem = Mapper.Map(employee);

    It should be :
    EmployeeViewItem employeeViewItem = Mapper.Map(employee);

    ReplyDelete
    Replies
    1. The braces were interpreted as HTML and cut off your code. ;)

      Rectifying:
      It should be:

      EmployeeViewItem employeeViewItem = Mapper.Map< Employee, EmployeeViewItem >(employee);

      Delete
  2. Easy to understand. Good job..

    ReplyDelete
  3. Thanks.It's very usefully for a newbie like me

    ReplyDelete
  4. my question is, there is way more code and more complicated then just the normal mapping. what am I missing?

    ReplyDelete
    Replies
    1. You're missing 2 things:
      1. Only the fields that need some kind of transformation are in the code. Everything else is inferred by Automapper
      2. You only need to define the mapper once, then you just call one line of code many times throughout your application (e.g., var myObject=Mapper.Map(myOtherObject); )

      Delete
  5. How to read List of Employees using auto mapper?

    ReplyDelete
  6. That's all right.
    But you lose references to the object.
    What if you change a property of an employee?

    ReplyDelete