Let’s see following code which you have written often to get this done:
var authorBooks = service.GetAuthorBooks();
foreach (var item in authorBooks)
{
var AuthorBookViewModel viewModel = new AuthorBookViewModel
{
Title = item.Title,
ISBN = item.ISBN,
PublishDate = item.PublishDate,
Publication = item.Publication,
Url = item.Url
Price = item.Price
};
this.BooksCollection.Add(AuthorBookViewModel);
}
Listing 1.0 Manual mapping of client/server entities
The code in Listing 1.0 goes well as long there are few properties. But when it comes
to map several properties like this, it becomes pain to do. Also when you add
properties into the entities, you again need to revisit such code and make appropriate
changes. Also if you need to use Mock data, you again write same code for mapping
mock object to view model object.
AutoMapper
AutoMapper is a mapper between two objects. It maps two different entities. To achieve
the same result as per Listing 1.0, you need to do two things:
1. Tell AutoMapper about the mapping relation.
Mapper.CreateMap<AuthorBookServiceModel, AuthorBookViewModel >();
2. Now use following code to get object of AuthorBookViewModel:
var viewModel =
Mapper.Map< AuthorBookServiceModel, AuthorBookViewModel>(item);
You see! Lot of reduction of the code. Also it would be much managed when you have
many objects with several properties to map. You can just create one class which
you call on startup of application that will register all the class mapping with
AutoMapper. And later a single place where you call out all the mapping and get
your objects transformed for request and response.
What we have seen so far is very basic usage of Automapper. Let’s start with some
real world scenarios to use it! First, create a C# Console application project.
Add Automapper NuGet. More information about Automapper NuGet package can be
found at:
https://www.nuget.org/packages/AutoMapper
Once we are done with AutoMapper NuGet package installation, Add following set of
classes and methods defined:
public class AuthorBookServiceModel
{
public string Author { get; set; }
public string Title { get; set; }
public string ISBN { get; set; }
public string Publication { get; set; }
public string Url { get; set; }
public double Price { get; set; }
public DateTime PublishDate { get; set; }
}
public class AuthorBookViewModel
{
public string AuthorName { get; set; }
public string Title { get; set; }
public string ISBN { get; set; }
public string Publication { get; set; }
public string Url { get; set; }
public double Price { get; set; }
public DateTime PublishDate { get; set; }
}
class Program
{
static void Main(string[] args)
{
var program = new Program();
Mapper.CreateMap<AuthorBookServiceModel, AuthorBookViewModel>();
program.Run();
}
private void Run()
{
AuthorBookServiceModel authorBook = GetAuthorBooks();
AuthorBookViewModel authorBookViewModel =
Mapper.Map<AuthorBookServiceModel, AuthorBookViewModel>(authorBook);
DisplayResult(authorBookViewModel);
Console.ReadLine();
}
private void DisplayResult(
AuthorBookViewModel authorBookViewItem)
{
Console.WriteLine("Mapped AuthorBookViewModel object" + Environment.NewLine);
Console.WriteLine(authorBookViewItem.Title);
Console.WriteLine(authorBookViewItem.AuthorName);
Console.WriteLine(authorBookViewItem.Price);
Console.WriteLine(authorBookViewItem.Url);
Console.WriteLine(authorBookViewItem.Publication);
Console.WriteLine(authorBookViewItem.ISBN);
Console.WriteLine(authorBookViewItem.PublishDate);
}
private AuthorBookServiceModel GetAuthorBooks()
{
return new AuthorBookServiceModel()
{
Author = "Ashish Ghoda",
Title = "Windows 8 MVVM Patterns Revealed",
Url = "http://www.apress.com/9781430249085",
ISBN = "978-1-4302-4908-5",
Price = 19.99,
Publication = "Apress",
PublishDate = new DateTime(2013, 1, 1)
};
}
}
Listing 1.1 Sample code for AutoMapper examples
When you run the code as per listing 1.1, you will see that it shows all the property’s
value displayed except AuthorName. This is because the other entity has Author
property and we have not told AutoMapper to map Author to AuthorName. To fix
this, we need to modify our mapping to following:
Mapper.CreateMap<AuthorBookServiceModel, AuthorBookViewModel>()
.ForMember(dest => dest.AuthorName, opt => opt.MapFrom(source
=> source.Author));
Listing 1.2 Mapping properties with different name
Note that mapping works one way. Means Your mapping AuthorBookServiceModel to AuthorBookViewModel.
So if you try to map reverse i.e. AuthorBookViewModel to AuthorBookServiceModel,
you will get an exception. To make it work both ways and also map properties
with different names, we need to use ReverseMap method as following:
Mapper.CreateMap<AuthorBookServiceModel, AuthorBookViewModel>()
.ForMember(dest => dest.AuthorName, opt => opt.MapFrom(source
=> source.Author)).ReverseMap();
Listing 1.3 Two-way mapping using ReverseMap
Let’s add property called FormattedPrice in AuthorBookViewModel. This will show $ sign with price of the book. To map it accordingly, we can do as per listing 1.4:
Mapper.CreateMap<AuthorBookServiceModel, AuthorBookViewModel>()
.ForMember(dest => dest.AuthorName, opt => opt.MapFrom(source
=> source.Author))
.ForMember(dest => dest.FormattedPrice, m => m.MapFrom(source
=> "$" + source.Price));
Listing 1.4 Map property with string manipulation
Many times it happens that you show meaningful value in UI for Boolean or other properties.
Like Yes/No, Available/Not Available for Boolean values. We will learn how to
map Boolean property to Text property via AutoMapper. For this, add IsAvailable
boolean property in AuthorBookServiceModel.
public bool IsAvailable { get; set; }
Also add Status string property in AuthorBookViewModel.
public string Status { get; set; }
Now let’s add ValueResolver for this as per following code:
public class StatusResolver : ValueResolver<bool, string>
{
protected override string ResolveCore(bool source)
{
return source ? "Available" : "No available";
}
}
Now listing 1.5 below shows how to use this StatusResolver with AutoMapper.
Mapper.CreateMap<AuthorBookServiceModel, AuthorBookViewModel>()
.ForMember(dest => dest.AuthorName, opt => opt.MapFrom(source
=> source.Author))
.ForMember(dest => dest.FormattedPrice, m => m.MapFrom(source
=> "$" + source.Price))
.ForMember(dest => dest.Status, c => c.ResolveUsing<StatusResolver>().FromMember(source
=> source.IsAvailable))
.ReverseMap();
Listing 1.5 Map using value resolver
Source code is available here.