ASP.NET Core  

Using AutoMapper in ASP.NET Core for Cleaner Full-Stack Code

When building full-stack applications with ASP.NET Core and Angular (or any frontend), you often deal with DTOs (Data Transfer Objects), ViewModels, and Entities. Mapping data between these objects manually can be repetitive, error-prone, and messy.

AutoMapper is a library that simplifies object-to-object mapping in ASP.NET Core. It automatically maps properties between objects with minimal configuration, making your backend code cleaner and maintainable.

This article explains how to use AutoMapper step by step in a full-stack application scenario.

What You Will Learn

  • What AutoMapper is and why it’s useful

  • How to install and configure AutoMapper in ASP.NET Core

  • How to create DTOs and map them to entities

  • How to integrate AutoMapper in your controllers and services

  • How AutoMapper improves frontend-backend communication

  • Best practices for clean, maintainable code

Step 1. Why Use AutoMapper

Suppose you have a User entity in your database:

public class User
{
    public int Id { get; set; }
    public string FullName { get; set; }
    public string Email { get; set; }
    public string PasswordHash { get; set; }
}

You don’t want to send the PasswordHash to the frontend. You may create a UserDto:

public class UserDto
{
    public string FullName { get; set; }
    public string Email { get; set; }
}

Without AutoMapper, you need to manually map:

var userDto = new UserDto
{
    FullName = user.FullName,
    Email = user.Email
};

For large objects or multiple endpoints, this becomes tedious. AutoMapper handles this automatically.

Part 1. Install Automapper

Run this command in your ASP.NET Core project:

dotnet add package AutoMapper.Extensions.Microsoft.DependencyInjection

Part 2. Create DTOs (Data Transfer Objects)

DTOs are lightweight objects that contain only the data you want to expose to the frontend.

Example

public class UserDto
{
    public string FullName { get; set; }
    public string Email { get; set; }
}

public class CreateUserDto
{
    public string FullName { get; set; }
    public string Email { get; set; }
    public string Password { get; set; }
}

Part 3. Configure Automapper

Create a Mapping Profile:

MappingProfile.cs

using AutoMapper;

public class MappingProfile : Profile
{
    public MappingProfile()
    {
        // Map User -> UserDto
        CreateMap<User, UserDto>();

        // Map CreateUserDto -> User
        CreateMap<CreateUserDto, User>()
            .ForMember(dest => dest.PasswordHash, opt => opt.MapFrom(src => BCrypt.Net.BCrypt.HashPassword(src.Password)));
    }
}

Step 2: Register AutoMapper in Startup

Program.cs (ASP.NET Core 6/7+)

using AutoMapper;

var builder = WebApplication.CreateBuilder(args);

// Add AutoMapper
builder.Services.AddAutoMapper(typeof(Program));

// Add other services
builder.Services.AddControllers();
builder.Services.AddDbContext<AppDbContext>(...);

var app = builder.Build();
app.MapControllers();
app.Run();

Part 4. Using Automapper in Controllers

Inject IMapper into your controller:

using AutoMapper;
using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
    private readonly AppDbContext _context;
    private readonly IMapper _mapper;

    public UsersController(AppDbContext context, IMapper mapper)
    {
        _context = context;
        _mapper = mapper;
    }

    // Get all users
    [HttpGet]
    public IActionResult GetUsers()
    {
        var users = _context.Users.ToList();
        var usersDto = _mapper.Map<List<UserDto>>(users);
        return Ok(usersDto);
    }

    // Create new user
    [HttpPost]
    public IActionResult CreateUser([FromBody] CreateUserDto createUserDto)
    {
        var user = _mapper.Map<User>(createUserDto);
        _context.Users.Add(user);
        _context.SaveChanges();

        var userDto = _mapper.Map<UserDto>(user);
        return Ok(userDto);
    }
}

Part 5. Benefits in Full-Stack Developement

  1. Cleaner backend code
    AutoMapper removes repetitive manual mapping, making your controllers concise.

  2. Consistent data shaping
    You can ensure all endpoints return consistent DTOs.

  3. Separation of concerns
    Entities remain database-focused; DTOs remain API-focused.

  4. Security
    You can exclude sensitive fields like PasswordHash without manually removing them.

  5. Easy maintenance
    Changing property names in the database requires only updating the mapping profile.

Part 6. Integrating with Angular

Suppose you have an Angular frontend:

Service to get users

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';

@Injectable({ providedIn: 'root' })
export class UserService {
  constructor(private http: HttpClient) {}

  getUsers() {
    return this.http.get<User[]>('https://localhost:5001/api/users');
  }

  createUser(user: any) {
    return this.http.post<User>('https://localhost:5001/api/users', user);
  }
}

Component to display users

export class UsersComponent implements OnInit {
  users: any[] = [];

  constructor(private userService: UserService) {}

  ngOnInit() {
    this.userService.getUsers().subscribe((res: any) => this.users = res);
  }
}

Because the backend uses AutoMapper to exclude sensitive fields, the frontend receives only the data it needs.

Part 7. Advanced mapping Techniques

1. Flattening Nested Objects

CreateMap<User, UserDto>()
    .ForMember(dest => dest.RoleName, opt => opt.MapFrom(src => src.Role.Name));

2. Conditional Mapping

CreateMap<User, UserDto>()
    .ForMember(dest => dest.Email, opt => opt.Condition(src => !string.IsNullOrEmpty(src.Email)));

3. Ignore Certain Properties

CreateMap<CreateUserDto, User>()
    .ForMember(dest => dest.Id, opt => opt.Ignore());

Part 8. Best Practices

  1. Use DTOs for all API responses

  2. Keep MappingProfiles organized by domain

  3. Avoid mapping large lists inside controller; map in services

  4. Use Flattening and Conditional mapping for complex objects

  5. Test your mappings with Unit Tests

Conclusion

Using AutoMapper in ASP.NET Core helps you:

  • Write cleaner, maintainable code

  • Avoid repetitive manual mapping

  • Separate your database entities from API responses

  • Safely send only necessary data to the frontend

For full-stack developers using Angular or React, AutoMapper ensures that your API consistently sends clean, secure, and structured data.