Component refactoring
.NET .NET Core C# ORM Patterns

How to Use AutoMapper in a .NET Core Application

Welcome to today’s post.

In today’s post I will be showing how to use an ORM mapping tool like AutoMapper to tidy up your mapping code within your application and give guidelines on whether to use a mapping tool.

AutoMapper is an ORM library that allows us to use the well-known Adapter design pattern without having to implement classes to map one DTO / model structure to another DTO / model structure.

The AutoMapper library has been available as either an installed library or as a NuGet package within Visual Studio since 2010, so it has been in wide use for many years.

Before I show how to use AutoMapper in your application, I will explain some reasons for using an ORM mapping tool in the next section.

Reasons for Using ORM Mapping Tools

Why should I use an ORM mapping tool within my application?

The reasons might be:

  1. To tidy up your mapping code. Manually crafted mapping repeated for the same view models and DTOs can look untidy.
  2. Reduce hard to find errors down the track caused by redundant fields.
  3. Localized mapping logic so that debugging is easier.
  4. Limiting your UI views and dependent APIs and applications to necessary data fields.

In addition to the above benefits, the benefit itself of ensuring that your code is cleaner and more readable, which is what I mentioned in one of my previous posts.

In the next section I will show how to install and configure AutoMapper in a .NET Core application, then proceed to show an example of its usage.

Installation and Configuration of AutoMapper in a .NET Core Application

To install AutoMapper within a .NET Core project you search for and install the following NuGet packages:

You will need to configure AutoMapper to be used within your .NET Core service collection. Within ConfigureServices() in your Startup.cs file:

using AutoMapper;
…
public void ConfigureServices(IServiceCollection services)
{
    …
    services.AddAutoMapper(typeof(Startup));
    …
}

The next configuration you will need to so is by creating a mapping profile. A mapping profile defines which objects you would like to map, and the member field of each source object you wish to map to a corresponding destination object member field.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using AutoMapper;
using BookLoan.Models;
	
namespace BookLoan.Loan.API.Mapping
{
    public class BookLoanMappingProfile : Profile
    {
        public BookLoanMappingProfile()
        {
            CreateMap<BookStatusViewModel, LoanReportDto>()
	            .ForMember(dest => dest.Status, 
                    opt => opt.MapFrom(src => src.Status))
	            .ForMember(dest => dest.Title, 
                    opt => opt.MapFrom(src => src.Title))
	            .ForMember(dest => dest.Borrower, 
                    opt => opt.MapFrom(src => src.Borrower))
	            .ForMember(dest => dest.DateLoaned, 
                    opt => opt.MapFrom(src => src.DateLoaned))
                .ForMember(dest => dest.DateReturn, 
                    opt => opt.MapFrom(src => src.DateReturn))
	            .ForMember(dest => dest.DateDue, 
                    opt => opt.MapFrom(src => src.DateDue));
        }
    }
}

As you can see, I have mapped several fields from the model class that contains the higher number of member fields to a model DTO class that contains a subset of the fields.

I have used the AutoMapper extension method ForMember() which allows us to chain together a number of mappings in the same mapping configuration.

Using the mapper within our classes requires us to inject the instance into the constructor. This is done using the IMapper interface.

public class ReportService : IReportService
{
    ...
    private readonly IMapper _mapper;

    public ReportService(
	    ...
        IMapper mapper)
    {
        ...
        _mapper = mapper;
    }
    ...
}

In the next section, I will show how to convert some existing code that is using a manual mapping technique to apply the cleaner technique with AutoMapper.

Moving from Manual Mapping to Auto Mapping

Before I show the code that uses manual mapping and the improved mapping, I will show the DTO classes that will be used in the mapping from the source DTO to the destination DTO.

In the mapping profile class, we have a pair of classes.

The model definition DTO for the source object is shown below:

public class BookStatusViewModel: BookViewModel
{
    public string Status { get; set; }
    public DateTime DateLoaned { get; set; }
    public DateTime DateDue { get; set; }
    public DateTime DateReturn { get; set; }
    public string Borrower { get; set; }
    public bool OnShelf { get; set; }
}

The model definition DTO for the destination object is shown below:

public class LoanReportDto
{
    public string Status { get; set; }
    public string Title { get; set; }
    public DateTime DateLoaned { get; set; }
    public DateTime DateDue { get; set; }
    public DateTime DateReturn { get; set; }
    public string Borrower { get; set; }
    public int DaysOverdue { get; set; }
}

Below is a method that contains manual mapping of the above classes:

private LoanReportDto BookToBookLoanStatus(BookViewModel input)
{
    BookLoan.Models.BookStatusViewModel bookStatus = 
        _loanService.GetBookLoanStatus(input.ID).GetAwaiter().GetResult();
         
    return new BookStatusViewModel()
    {
        ID = input.ID,
        Author = input.Author,
        Title = input.Title,
        Genre = input.Genre,
        ISBN = input.ISBN,
        Edition = input.Edition,
        Location = input.Location,
        YearPublished = input.YearPublished,
        OnShelf = bookStatus.OnShelf,
        DateLoaned = bookStatus.DateLoaned,
        DateReturn = bookStatus.DateReturn,
        DateDue = bookStatus.DateDue,
        Status = bookStatus.Status,
        Borrower = bookStatus.Borrower
    };
}

After reviewing the dependencies of the above method, we decide to use AutoMapper to remap the above to a subset of the above fields in the LoanReportDto class. To execute a mapping we use the Map() method which is defined as follows:

  var dto =_mapper.Map<DestinationDto>(SourceDto);

Where:

SourceDto is the source mapping object.

DestinationDto is the destination mapping object.

dto is the resulting mapping object.

After applying AutoMapper our method looks as shown:

private LoanReportDto BookToBookLoanStatus(BookViewModel input)
{
    BookLoan.Models.BookStatusViewModel bookStatus = 
        _loanService.GetBookLoanStatus(input.ID).GetAwaiter().GetResult();
    bookStatus.Title = input.Title;

    var loanReport =_mapper.Map<LoanReportDto>(bookStatus);
            
    return loanReport;
}

After running our application and debugging we notice that the source mapping for the book loan status is shown with the three source date mapping fields highlighted:

Following the mapping, the same source date fields have been mapping into the destination object as shown:

After the mapping, our code is cleaner and easier to understand.

There are many more configurations we can apply to our mappings including conditional mappings and mappings of lists, custom resolvers and so on.

More details on AutoMapper and numerous examples can be found on the AutoMapper site.

That’s all for today’s post.

I hope you found this post useful and informative.

Social media & sharing icons powered by UltimatelySocial