Skip to content

Instantly share code, notes, and snippets.

@pazteddy
Created May 15, 2026 16:24
Show Gist options
  • Select an option

  • Save pazteddy/0cbfb1f977b3494fdbcdab0a23c691d5 to your computer and use it in GitHub Desktop.

Select an option

Save pazteddy/0cbfb1f977b3494fdbcdab0a23c691d5 to your computer and use it in GitHub Desktop.
Guía de Migración: AutoMapper → Mapster

Guía de Migración: AutoMapper → Mapster

Proyecto cs-apiEcommerce · Sección 15

Commit de referencia: cd81803
Stack: ASP.NET Core 8 · Entity Framework Core · API Versioning · JWT


¿Por qué migrar a Mapster?

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

Paso 1 — Actualizar el .csproj

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.DependencyInjection

Paso 2 — Crear Mapping/MapsterConfig.cs

Reemplaza 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 → TDest y TDest → TSource).


Paso 3 — Vaciar los perfiles de AutoMapper

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.


Paso 4 — Registrar Mapster en Program.cs

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();

Paso 5 — Limpiar los controladores

Este es el cambio más repetitivo. Se aplica igual en los cuatro controladores:
ProductsController, UsersController, V1/CategoriesController y V2/CategoriesController.

5.1 — Cambiar el using

// Antes
using AutoMapper;

// Después
using Mapster;

5.2 — Eliminar la dependencia IMapper del constructor

// 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;
}

5.3 — Reemplazar las llamadas de mapeo

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>();

Tabla de equivalencias completa

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>>()

Resumen de archivos modificados

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 AddAutoMapperAddMapster + 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

Equivalencias de configuración AutoMapper → Mapster

Mapeo simple bidireccional

// AutoMapper
CreateMap<Category, CategoryDto>().ReverseMap();

// Mapster
TypeAdapterConfig<Category, CategoryDto>.NewConfig().TwoWays();

Mapeo con propiedad personalizada

// 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();

Verificación post-migración

# 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/users

Si todos los endpoints devuelven los DTOs correctamente poblados (incluyendo CategoryName en productos), la migración fue exitosa.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment