π§Ύ DTOs and AutoMapper in .NET Core β Explained in Detail
β What is a DTO?
DTO stands for Data Transfer Object.
β€ Purpose:
- A DTO is a simple object used to transfer data between layers (usually between backend and frontend).
- It hides internal models (e.g., EF Core entities) from the API response to keep your architecture clean and secure.
β Example: Why Use DTOs?
Letβs say you have an entity class:
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public string PasswordHash { get; set; } // Shouldn't be exposed!
public DateTime DateOfBirth { get; set; }
}
If you return this directly in your API, the PasswordHash
would be exposed π±.
β Instead, use a DTO:
public class UserDto
{
public string Username { get; set; }
public int Age { get; set; }
}
Now the API only returns whatβs relevant and safe.
π What is AutoMapper?
AutoMapper is a library that automatically maps one object type to another, like mapping an Entity to a DTO.
Without AutoMapper:
var userDto = new UserDto
{
Username = user.Username,
Age = CalculateAge(user.DateOfBirth)
};
With AutoMapper:
var userDto = _mapper.Map<UserDto>(user);
π οΈ How to Use AutoMapper in .NET Core
1. π¦ Install Package:
dotnet add package AutoMapper.Extensions.Microsoft.DependencyInjection
2. π§ Create a Mapping Profile
public class AutoMapperProfiles : Profile
{
public AutoMapperProfiles()
{
CreateMap<User, UserDto>()
.ForMember(dest => dest.Age,
opt => opt.MapFrom(src => CalculateAge(src.DateOfBirth)));
}
private int CalculateAge(DateTime dob)
{
var today = DateTime.Today;
var age = today.Year - dob.Year;
if (dob.Date > today.AddYears(-age)) age--;
return age;
}
}
3. π§ Register AutoMapper in Program.cs
or Startup.cs
builder.Services.AddAutoMapper(typeof(AutoMapperProfiles).Assembly);
4. π§ͺ Use AutoMapper in Controller or Service
private readonly IMapper _mapper;
public UsersController(IMapper mapper)
{
_mapper = mapper;
}
[HttpGet("{id}")]
public async Task<ActionResult<UserDto>> GetUser(int id)
{
var user = await _context.Users.FindAsync(id);
return _mapper.Map<UserDto>(user);
}
π§ Benefits of Using DTOs + AutoMapper
Benefit | Description |
---|---|
π Security | Prevent exposing sensitive fields like passwords |
π― Performance | Transfer only what is needed |
β»οΈ Decoupling | Prevent tight coupling between API and DB models |
βοΈ Reusability | Use different DTOs for different views (e.g., UserListDto , UserDetailsDto ) |
β± Saves Time | AutoMapper reduces boilerplate mapping code |
β Summary:
Concept | Purpose |
---|---|
DTO | Simple object for data transfer between layers, hides domain internals |
AutoMapper | Tool to automatically map between objects (e.g., Entity β DTO) |
Hereβs a complete working example of using DTOs and AutoMapper in a minimal .NET Core Web API project.
β Example: DTO + AutoMapper in .NET 6/7 API
π§ Step 1: Create a New Web API Project
dotnet new webapi -n UserApiDemo
cd UserApiDemo
π¦ Step 2: Install AutoMapper Package
dotnet add package AutoMapper.Extensions.Microsoft.DependencyInjection
π§βπ» Step 3: Create the Entity (User.cs
)
// Models/User.cs
namespace UserApiDemo.Models;
public class User
{
public int Id { get; set; }
public string Username { get; set; } = string.Empty;
public string PasswordHash { get; set; } = string.Empty;
public DateTime DateOfBirth { get; set; }
}
π§Ύ Step 4: Create the DTO (UserDto.cs
)
// Dtos/UserDto.cs
namespace UserApiDemo.Dtos;
public class UserDto
{
public string Username { get; set; } = string.Empty;
public int Age { get; set; }
}
π Step 5: Create the Mapping Profile
// Helpers/AutoMapperProfiles.cs
using AutoMapper;
using UserApiDemo.Models;
using UserApiDemo.Dtos;
namespace UserApiDemo.Helpers;
public class AutoMapperProfiles : Profile
{
public AutoMapperProfiles()
{
CreateMap<User, UserDto>()
.ForMember(dest => dest.Age,
opt => opt.MapFrom(src => CalculateAge(src.DateOfBirth)));
}
private int CalculateAge(DateTime dob)
{
var today = DateTime.Today;
var age = today.Year - dob.Year;
if (dob.Date > today.AddYears(-age)) age--;
return age;
}
}
ποΈ Step 6: Register AutoMapper in Program.cs
// Program.cs
using UserApiDemo.Helpers;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddAutoMapper(typeof(AutoMapperProfiles).Assembly);
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
app.UseSwagger();
app.UseSwaggerUI();
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
π Step 7: Create Controller
// Controllers/UsersController.cs
using Microsoft.AspNetCore.Mvc;
using UserApiDemo.Models;
using UserApiDemo.Dtos;
using AutoMapper;
namespace UserApiDemo.Controllers;
[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
private readonly IMapper _mapper;
public UsersController(IMapper mapper)
{
_mapper = mapper;
}
[HttpGet("{id}")]
public ActionResult<UserDto> GetUser(int id)
{
// Dummy user data (replace with DB logic in real app)
var user = new User
{
Id = id,
Username = "john_doe",
PasswordHash = "encrypted",
DateOfBirth = new DateTime(1995, 5, 10)
};
var userDto = _mapper.Map<UserDto>(user);
return Ok(userDto);
}
}
β
Output Example from GET /api/users/1
{
"username": "john_doe",
"age": 30
}
π Summary
This project:
- Defines an entity (
User
) - Hides sensitive info using a DTO (
UserDto
) - Maps them using AutoMapper
- Returns clean, safe API responses
Happy Coding!
Top comments (0)