Commit de referencia:
cd81803
Stack: ASP.NET Core 8 · Entity Framework Core · API Versioning · JWT
| AutoMapper | Mapster | |
|---|---|---|
| Rendimiento | Reflexión en runtime | Generación de código en compile-time |
| Configuración | Perfiles heredados (Profile) |
Clase estática simple |
| DI en controladores | Requiere inyectar IMapper |
Sin dependencia en el constructor |
| Sintaxis de uso | _mapper.Map<T>(obj) |
obj.Adapt<T>() (extension method) |
| Tamaño del paquete | Mayor | Más liviano |
Comentar (o eliminar) AutoMapper e instalar los dos paquetes de Mapster:
<!-- ApiEcommerce.csproj -->
<!-- Remover o comentar: -->
<!-- <PackageReference Include="AutoMapper" Version="14.0.0" /> -->
<!-- Agregar: -->
<PackageReference Include="Mapster" Version="7.4.0" />
<PackageReference Include="Mapster.DependencyInjection" Version="1.0.1" />Desde la terminal:
dotnet remove package AutoMapper
dotnet add package Mapster
dotnet add package Mapster.DependencyInjectionReemplaza los perfiles de AutoMapper con una única clase estática centralizada. Cada mapeo usa TypeAdapterConfig<TSource, TDest>.NewConfig().TwoWays() para habilitar el mapeo bidireccional automáticamente.
// Mapping/MapsterConfig.cs
using ApiEcommerce.Models;
using ApiEcommerce.Models.Dtos;
using Mapster;
namespace ApiEcommerce.Mapping;
public static class MapsterConfig
{
public static void RegisterMappings()
{
// ── User ──────────────────────────────────────────────────────────────
TypeAdapterConfig<User, UserDto>.NewConfig().TwoWays();
TypeAdapterConfig<User, CreateUserDto>.NewConfig().TwoWays();
TypeAdapterConfig<User, UserLoginDto>.NewConfig().TwoWays();
TypeAdapterConfig<User, UserLoginResponseDto>.NewConfig().TwoWays();
TypeAdapterConfig<ApplicationUser, UserDataDto>.NewConfig().TwoWays();
TypeAdapterConfig<ApplicationUser, UserDto>.NewConfig().TwoWays();
// ── Product ───────────────────────────────────────────────────────────
// Mapeo con propiedad calculada: CategoryName viene de la relación Category.Name
TypeAdapterConfig<Product, ProductDto>.NewConfig()
.Map(dest => dest.CategoryName, src => src.Category.Name)
.TwoWays();
TypeAdapterConfig<Product, CreateProductDto>.NewConfig().TwoWays();
TypeAdapterConfig<Product, UpdateProductDto>.NewConfig().TwoWays();
// ── Category ──────────────────────────────────────────────────────────
TypeAdapterConfig<Category, CategoryDto>.NewConfig().TwoWays();
TypeAdapterConfig<Category, CreateCategoryDto>.NewConfig().TwoWays();
}
}Nota sobre
.TwoWays(): equivale al.ReverseMap()de AutoMapper. Registra automáticamente el mapeo en ambas direcciones (TSource → TDestyTDest → TSource).
Los archivos CategoryProfile.cs, ProductProfile.cs y UserProfile.cs quedan como referencia histórica, pero sin código activo:
// Mapping/CategoryProfile.cs
// Mapeos migrados a Mapster. Archivo dejado vacío para referencia.// Mapping/ProductProfile.cs
// Mapeos migrados a Mapster. Archivo dejado vacío para referencia.// Mapping/UserProfile.cs
// Mapeos migrados a Mapster. Archivo dejado vacío para referencia.Puedes eliminar estos archivos si prefieres mantener el proyecto limpio.
Reemplazar AddAutoMapper por AddMapster y llamar al registro de configuraciones:
// Program.cs — antes
builder.Services.AddAutoMapper(typeof(Program).Assembly);// Program.cs — después
using Mapster;
using ApiEcommerce.Mapping;
// ...
builder.Services.AddMapster();
// Registrar los mapeos personalizados
MapsterConfig.RegisterMappings();Este es el cambio más repetitivo. Se aplica igual en los cuatro controladores:
ProductsController, UsersController, V1/CategoriesController y V2/CategoriesController.
// Antes
using AutoMapper;
// Después
using Mapster;// Antes
private readonly IProductRepository _productRepository;
private readonly ICategoryRepository _categoryRepository;
private readonly IMapper _mapper;
public ProductsController(
IProductRepository productRepository,
ICategoryRepository categoryRepository,
IMapper mapper)
{
_productRepository = productRepository;
_categoryRepository = categoryRepository;
_mapper = mapper;
}// Después — sin IMapper en ningún lado
private readonly IProductRepository _productRepository;
private readonly ICategoryRepository _categoryRepository;
public ProductsController(
IProductRepository productRepository,
ICategoryRepository categoryRepository)
{
_productRepository = productRepository;
_categoryRepository = categoryRepository;
}Todos los _mapper.Map<T>(source) se convierten en source.Adapt<T>():
// Antes
var productsDto = _mapper.Map<List<ProductDto>>(products);
var productDto = _mapper.Map<ProductDto>(product);
var product = _mapper.Map<Product>(createProductDto);// Después
var productsDto = products.Adapt<List<ProductDto>>();
var productDto = product.Adapt<ProductDto>();
var product = createProductDto.Adapt<Product>();| AutoMapper | Mapster |
|---|---|
_mapper.Map<ProductDto>(product) |
product.Adapt<ProductDto>() |
_mapper.Map<List<ProductDto>>(products) |
products.Adapt<List<ProductDto>>() |
_mapper.Map<Product>(createProductDto) |
createProductDto.Adapt<Product>() |
_mapper.Map<CategoryDto>(category) |
category.Adapt<CategoryDto>() |
_mapper.Map<Category>(createCategoryDto) |
createCategoryDto.Adapt<Category>() |
_mapper.Map<UserDto>(user) |
user.Adapt<UserDto>() |
_mapper.Map<List<UserDto>>(users) |
users.Adapt<List<UserDto>>() |
| Archivo | Acción |
|---|---|
ApiEcommerce.csproj |
Comentar AutoMapper, agregar Mapster + DI |
Mapping/MapsterConfig.cs |
Crear — configuración centralizada de mapeos |
Mapping/CategoryProfile.cs |
Vaciar (eliminar clase CategoryProfile) |
Mapping/ProductProfile.cs |
Vaciar (eliminar clase ProductProfile) |
Mapping/UserProfile.cs |
Vaciar (eliminar clase UserProfile) |
Program.cs |
AddAutoMapper → AddMapster + MapsterConfig.RegisterMappings() |
Controllers/ProductsController.cs |
Quitar IMapper, reemplazar .Map<>() por .Adapt<>() |
Controllers/UsersController.cs |
Ídem |
Controllers/V1/CategoriesController.cs |
Ídem |
Controllers/V2/CategoriesController.cs |
Ídem |
// AutoMapper
CreateMap<Category, CategoryDto>().ReverseMap();
// Mapster
TypeAdapterConfig<Category, CategoryDto>.NewConfig().TwoWays();// AutoMapper
CreateMap<Product, ProductDto>()
.ForMember(dest => dest.CategoryName, opt => opt.MapFrom(src => src.Category.Name))
.ReverseMap();
// Mapster
TypeAdapterConfig<Product, ProductDto>.NewConfig()
.Map(dest => dest.CategoryName, src => src.Category.Name)
.TwoWays();# 1. Compilar el proyecto
dotnet build
# 2. Ejecutar la aplicación
dotnet run
# 3. Probar endpoints en Swagger o con curl
curl http://localhost:{PORT}/api/v1/categories
curl http://localhost:{PORT}/api/products
curl http://localhost:{PORT}/api/usersSi todos los endpoints devuelven los DTOs correctamente poblados (incluyendo CategoryName en productos), la migración fue exitosa.