Getting Started with AutoMapper in ASP.NET Core

fullstackhero
9 min readJun 21, 2020
Getting Started with AutoMapper in ASP.NET Core

In this article, we are going to learn how to use AutoMapper in an ASP.NET Core application.

We are going to start by looking into what the AutoMapper is and what problem it solves. Then, we are going to explain how we can use AutoMapper in our MVC application. After that, we’ll learn about the usage guidelines and best practices. We’ll also take a look at what’s happening behind the scenes and how to flatten complex object models.

You can find the source code on our GitHub repository.

VIDEO: Getting Started with AutoMapper in ASP.NET Core video.

https://www.youtube.com/watch?v=ncRjahgY6hs

Let’s learn what problems we can solve with AutoMapper.

What is AutoMapper?

AutoMapper is a simple library that helps us to transform one object type to another. It is a convention-based object-to-object mapper that requires very little configuration.

The object-to-object mapping works by transforming an input object of one type into an output object of a different type.

One Use Case

AutoMapper was built to solve a complex problem that most developers face in their day-to-day life — writing code that maps one object type to another. This type of code is rather tedious and boring to write, so why not leave that job to this little tool?

What makes AutoMapper interesting is that it provides some easy to use conventions to take the dirty work out of figuring out how to map Type A to Type B. As long as Type B follows AutoMapper’s established conventions, almost no configuration is needed to map two types.

Here’s one common scenario. We’ve created an application and we want to keep the separation between our domain models and our view models.

In order to accomplish this, we need to write the code to adapt our domain model to our view model. Then, as we add more views and domain models, we end up writing more adapters. Later on, we’ll have to write even more adapters to map our data transfer objects from the database layer into our domain objects.

This is mundane and repetitive. And this is where AutoMapper comes in.

How to Use AutoMapper in Our Application

Let’s have a look at how to add Automapper into our .NET Core application.

Installation

The first step is to install the corresponding NuGet package:

Install-Package AutoMapper.Extensions.Microsoft.DependencyInjection

If we install the AutoMapper.Extensions.Microsoft.DependencyInjection package, it will automatically install the AutoMapper package for us since it references it.

Configuration

After installing the required package, the next step is to configure the services. Let’s do it in the Startup.cs class:

public void ConfigureServices(IServiceCollection services){services.AddAutoMapper(typeof(Startup));services.AddControllersWithViews();}

That’s it. AutoMapper is installed and configured in our project. Now, let’s see how to use it with our objects.

Usage

Let’s say we have a domain object named User:

public class User
{
public int Id { get; set; }
public string FirstName { get; set; } public string LastName { get; set; } public string Email { get; set; } public string Address { get; set; }
}

In the UI layer, we would have a ViewModel to display the user information:

public class UserViewModel
{
public string FirstName { get; set; }
public string LastName { get; set; } public string Email { get; set; }
}

Now, let’s see how we are going to convert our domain object to a view model.

Profiles

A good way to organize our mapping configurations is with Profiles. We need to create classes that inherit from Profile class and put the configuration in the constructor:

public UserProfile()
{
CreateMap<User, UserViewModel>();
}

UserProfile class creates the mapping between our User domain object and UserViewModel. As soon as our application starts and initializes AutoMapper, AutoMapper will scan our application and look for classes that inherit from the Profile class and load their mapping configurations.

Now, let’s define a Controller and use the Auto-Mapping capabilities that we just added:

public class UserController : Controller
{
private readonly IMapper _mapper;
public UserController(IMapper mapper)
{
_mapper = mapper;
}
public IActionResult Index()
{
// Populate the user details from DB
var user = GetUserDetails();
UserViewModel userViewModel = _mapper.Map<UserViewModel>(user);return View(userViewModel);
}
}

First, we inject the mapper object into the controller. Then, we call the Map() method, which maps the User object to the UserViewModel object. Furthermore, pay attention to a local method GetUserDetails that we use for the local data storage. You can find its implementation in our source code.

Let’s create a View for the Index action method as explained in the article’s section: Creating Views.

Now let’s run the application:

We can see our User domain object is properly mapped to the UserViewModel object.

Mapping to Properties with different Names

Well, that was quite simple, wasn’t it?

But what if we have different property names in our source and destination objects. Let’s take a look at how to do the mapping in these cases.

Let’s modify the property names in UserViewModel class:

public class UserViewModel
{
public string FName { get; set; }
public string LName { get; set; }public string Email { get; set; }}

Here we need to map the below properties from User domain object to our UserViewModel:

User.FirstName -> UserViewModel.FName

User.LastName -> UserViewModel.LName

User.EMail -> UserViewModel.Email

So, let’s change the mapping in the UserProfile class:

public UserProfile()
{
CreateMap<User, UserViewModel>()
.ForMember(dest =>
dest.FName,
opt => opt.MapFrom(src => src.FirstName))
.ForMember(dest =>
dest.LName,
opt => opt.MapFrom(src => src.LastName))
}

We use the CreateMap() method to create a mapping by providing the source and destination properties.

If we want to customize the configuration for individual members, we can use the ForMember() method which has the parameters destinationMember, which is of type Expression and memberOptions, which is of type Action.

For example, the above code maps FirstName and LastName properties of the User object to the FName and the LName property of UserViewModel respectively.

After making these changes, let’s run the application once again. We can see that these properties are mapped correctly.

Reverse Mapping

So far, we have only looked at one-directional mapping, which means if we have two types Type A and Type B, then we only map Type A to Type B. But, by using Automapper’s Reverse mapping capability, it is possible to achieve bi-directional mapping:

public UserProfile()
{
CreateMap<User, UserViewModel>()
.ForMember(dest =>
dest.FName,
opt => opt.MapFrom(src => src.FirstName))
.ForMember(dest =>
dest.LName,
opt => opt.MapFrom(src => src.LastName))
.ReverseMap();
}

Once Reverse Mapping is configured, we can map back from destination to source type:

var mappedUser = _mapper.Map<User>(userViewModel);

This way, we can easily achieve bi-directional mapping between types using AutoMapper’s Reverse Mapping capabilities.

Just one note. We can use AutoMapper in the same manner in the ASP.NET Core Web API project as well, the implementation is the same just we wouldn’t map to the view models but, for example, our DTO classes.

Behind the Scenes

We have seen the magic of AutoMapper in action. So what happens in the background?

AutoMapper uses a programming concept called Reflection to retrieve the type metadata of objects. We can use reflection to dynamically get the type from an existing object and invoke its methods or access its fields and properties.

Then, based on the conventions and configurations defined, we can easily map the properties of the two types. AutoMapper was built around this concept.

Usage Guidelines and Best practices

As with every other component that we use in our application, there are certain usage guidelines and best practices that we need to follow while using AutoMapper.

Do’s

  • Always use the AutoMapper.Extensions.Microsoft.DependencyInjection package in ASP.NET Core with services.AddAutoMapper(assembly[]). This package will perform all the scanning and dependency injection registration. We only need to declare the Profile configurations.
  • Always organize configuration into Profiles. Profiles allow us to group common configuration and organize mappings by usage. This lets us put mapping configuration closer to where it is used, instead of a single file of configuration that becomes difficult to edit/maintain.
  • Always use configuration options supported by LINQ over their counterparts as LINQ query extensions have the best performance of any mapping strategy.
  • Always flatten DTOs. AutoMapper can handle mapping properties A.B.C into ABC. By flattening our model, we create a more simplified object that won’t require a lot of navigation to get at data.
  • Always put common simple computed properties into the source model. Similarly, we need to place computed properties specific to the destination model in the destination model.

Don’ts

  • Do not call CreateMap() on each request. It is not a good practice to create the configuration for each mapping request. Mapping configuration should be done once at startup.
  • Do not use inline maps. Inline maps may seem easier for very simple scenarios, but we lose the ease of configuration.
  • If we have to write a complex mapping behavior, it might be better to avoid using AutoMapper for that scenario.
  • Do not put any logic that is not strictly mapping behavior into the configuration. AutoMapper should not perform any business logic, it should only handle the mapping.
  • Avoid sharing DTOs across multiple maps. Model your DTOs around individual actions, and if you need to change it, you only affect that action.
  • Do not create DTOs with circular associations. AutoMapper does support it, but it’s confusing and can result in quite bad performance. Instead, we can create separate DTOs for each level of a hierarchy we want.

Flattening Complex Object Models

AutoMapper supports flattening complex object models into DTO or another simple object model. For example, Domain Objects usually have a complex object model with many associations between them, but ViewModels generally have a flat object model.

We can map Domain Objects to ViewModels with AutoMapper’s Flattening.

If we follow proper naming conventions for our object models, then there is no need to provide any additional configuration code. AutoMapper works with conventions and maps our object model from complex to flat/simple ones.

AutoMapper uses the following conventions:

  • It will automatically map properties with the same names.
  • If the source object has some association with other objects, then it will try to map with properties on the destination object whose name is a combination of the source class name and property name in the Pascal case.
  • It will try to map methods on source object which has a Get prefix with a property on destination object with the name excluding the Get prefix.

If we follow these conventions, AutoMapper will automatically map our objects. Otherwise, we’ll need to configure AutoMapper using Fluent API.

Let’s modify our User object by adding a child object Address:

public class User
{
public int Id { get; set; }
public string FirstName { get; set; }public string LastName { get; set; }public string Email { get; set; }public Address Address { get; set; }public string GetFullName()
{
return $"{this.LastName}, {this.FirstName}";
}
}

And here’s how the Address class looks like:

public class Address
{
public int Id { get; set; }
public string Door { get; set; }public string Street1 { get; set; }public string Street2 { get; set; }public string City { get; set; }public string State { get; set; }public string Country { get; set; }public string ZipCode { get; set; }
}

Also, note that we have added a method GetFullName() to get the user’s full name.

Let’s modify the UserViewModel class:

public class UserViewModel
{
[Display(Name = "Full Name")]
public string FullName { get; set; }
[Display(Name = "Country")]
public string AddressCountry { get; set; }
public string Email { get; set; }
}

Now, Let’s modify the profile class to use the default conventions:

public UserProfile()
{
CreateMap<User, UserViewModel>();
}

Let’s run the application once again:

This time, we can see that the GetFullName() method on the source object is correctly mapped to FullName property on the destination object.

Similarly, User.Address.Country property is automatically mapped to UserViewModel.AddressCountry.

These mappings are correctly handled by the AutoMapper using its default conventions.

Conclusion

In this article, we have learned the following concepts:

  • The AutoMapper component — what is it and when to use it?
  • Installing, Configuring and using the AutoMapper component in our ASP.NET Core application
  • How AutoMapper works behind the scenes
  • Usage Guidelines and best practices while using AutoMapper.
  • Flattening a complex object model using AutoMapper’s default conventions.

That’s all for now. Hope you enjoyed the article. We’ll cover more advanced topics related to AutoMapper in one of the following articles.

Original article

https://code-maze.com/automapper-net-core/

--

--

fullstackhero

All the knowledge I have today is due to doing it over and over again, I practice it wherever I go, regardless of time. My starting point comes from “passion”