Mapster gana esta comparación en la mayoría de los proyectos nuevos: es más rápido en todos los casos que medimos más abajo, necesita menos configuración en las situaciones habituales y sigue teniendo licencia MIT sin ninguna condición de elegibilidad.
AutoMapper cambió de licencia con la versión 15.0.0, en julio de 2025. El cambio no fue a una licencia “comercial”, sino a una doble licencia. Podemos acogernos a la Reciprocal Public License 1.5 y publicar nuestro propio código fuente bajo ella, o adoptar la licencia comercial, que es gratuita en un nivel Community solo si se cumplen a la vez sus cuatro condiciones. La versión 14.0.0 y todas las anteriores, hasta la 2.0.0, mantienen la licencia MIT de forma permanente.
AutoMapper sigue ganándose su lugar en las bases de código que ya lo usan de forma intensiva: es maduro, está documentado en todas partes y migrar tiene un costo real.
Veamos en qué se diferencian, característica por característica, con mediciones.
¿Por qué comparar AutoMapper vs Mapster?
AutoMapper es una de las bibliotecas de mapeo de objeto a objeto más populares, con más de 1100 millones de descargas del paquete NuGet. Se publicó por primera vez en 2009 y su uso no ha dejado de crecer desde entonces.
Mapster es una alternativa a AutoMapper, publicada por primera vez en 2015, con más de 76 millones de descargas del paquete NuGet. Aunque su número de descargas está muy lejos del de AutoMapper, se diseñó para ser eficiente tanto en velocidad como en memoria.
A juzgar por el historial de commits de AutoMapper y de Mapster, vemos que ambos proyectos se mantienen activamente.
Además, ambos ofrecen opciones de configuración para casos de mapeo que van de los sencillos a los más avanzados.
Hoy en día, la mayor diferencia entre ambos es la licencia. Desde la versión 15.0.0, publicada en julio de 2025, AutoMapper (junto con MediatR, ambos mantenidos por Lucky Penny Software) tiene doble licencia. Podemos aceptar la Reciprocal Public License 1.5, que es de código abierto y recíproca, lo que significa que publicamos nuestro propio código fuente bajo ella y no pagamos nada, o bien adoptar una licencia comercial. La versión 14.0.0 y todas las anteriores, hasta la 2.0.0, mantienen la licencia MIT de forma permanente, así que fijar la dependencia en [14.0.0, 15.0.0) nos deja un AutoMapper totalmente permisivo. Sin embargo, NuGet marca la versión 14.0.0 con un aviso de seguridad de gravedad alta (CVE-2026-32933, una denegación de servicio), y la corrección se publicó por primera vez en las versiones 15.1.1 y 16.1.1, que tienen doble licencia. Lucky Penny no ofrece actualizaciones de seguridad para las versiones anteriores con licencia MIT.
La parte comercial incluye un nivel Community gratuito, pero solo para personas y organizaciones que cumplan a la vez las cuatro condiciones del proveedor: ingresos brutos anuales (o presupuesto, en organizaciones sin fines de lucro) inferiores a 5 millones de dólares, no más de 10 millones de dólares de capital externo, no ser una entidad gubernamental o cuasigubernamental y no ser una universidad que use la biblioteca para software institucional u operativo. Si no se cumple aunque sea una condición, se aplican los niveles de pago: 499, 1499 o 3999 dólares al año, por producto, para las ediciones Standard, Professional y Enterprise.
Mapster no tiene nada de esto. Sigue con licencia MIT sin condiciones de elegibilidad, sin requisito de ingresos y sin niveles, así que es gratuito sea cual sea el tamaño de la empresa. Las condiciones y los precios de AutoMapper citados arriba son los de Lucky Penny Software, tomados de sus preguntas frecuentes sobre licencias y de su página de precios, consultadas el 13 de septiembre de 2026.
Vamos a empezar comparando Mapster con AutoMapper en algunos de los casos de mapeo de objeto a objeto más habituales.
Mapeo de tipos simples
En este caso, los tipos de origen y de destino tienen propiedades similares. Los nombres de las propiedades son siempre los mismos. Sin embargo, podemos usar tipos de datos distintos para las propiedades. Aquí el destino es un DTO. Si no tienes claro qué relación tiene con un objeto normal, consulta la diferencia entre un DTO y un POCO.
Para demostrarlo, vamos a crear un tipo de origen sencillo, User:
public class User
{
public int Id { get; set; }
public string Name { get; set; } = null!;
public bool IsActive { get; set; }
public string Email { get; set; } = null!;
public DateTime CreatedAt { get; set; }
}
Y después, vamos a crear el tipo de destino, UserDto:
public class UserDto
{
public int Id { get; set; }
public string Name { get; set; } = null!;
public bool IsActive { get; set; }
public string Email { get; set; } = null!;
public string CreatedAt { get; set; } = null!;
}
Aquí, todas las propiedades son similares a las del tipo de origen, excepto la propiedad CreatedAt. La propiedad CreatedAt tiene string como tipo de destino, mientras que DateTime es el tipo de origen. En situaciones como esta, las bibliotecas de mapeo hacen la conversión de tipos automáticamente.
Ambas bibliotecas admiten la conversión entre tipos primitivos.
Mapeo de tipos simples con AutoMapper
Para usar AutoMapper, primero tenemos que crear el objeto IMapper. Hay varias formas de crearlo. Una de ellas es usar la clase MapperConfiguration:
var mapper = new MapperConfiguration(cfg => cfg.CreateMap<User, UserDto>(), NullLoggerFactory.Instance).CreateMapper();
Desde la versión 15.0.0, el constructor de MapperConfiguration requiere un ILoggerFactory. Aquí pasamos NullLoggerFactory.Instance de Microsoft.Extensions.Logging.Abstractions, mientras que una aplicación ASP.NET Core obtendría la fábrica mediante inyección de dependencias. Los fragmentos de código de este artículo también necesitan los paquetes NuGet AutoMapper y Mapster, además de directivas using para los espacios de nombres AutoMapper, Mapster y Microsoft.Extensions.Logging.Abstractions.
De esta forma, tenemos que indicar el tipo de origen y el de destino como parámetros de tipo del método CreateMap<TSource, TDestination>(). En nuestro caso, User es el tipo de origen y UserDto, el de destino.
La otra forma de configurar el mapeador es usar perfiles. Podemos ver más detalles sobre ella en este artículo.
Antes de seguir, vamos a crear el objeto de origen con el tipo User:
var source = new User
{
Id = 1,
Name = "User 1",
Email = "[email protected]",
IsActive = true,
CreatedAt = DateTime.Now
};
Una vez que tenemos el objeto de origen y la instancia de IMapper, ya podemos mapear fácilmente al objeto de destino:
UserDto destination = mapper.Map<UserDto>(source);
En este caso, tenemos que pasar al método Map<TDestination>(object source) el objeto de origen como parámetro e indicar el tipo de destino como parámetro de tipo genérico.
Si inspeccionamos los valores de la variable destination, vemos que todos los valores de las propiedades del tipo de origen se han mapeado correctamente a las propiedades del tipo de destino:
{"Id":1,"Name":"User 1","IsActive":true,"Email":"[email protected]","CreatedAt":"01-09-2022 21:53:57"}
La propiedad CreatedAt también se mapea mediante la conversión automática de tipos de DateTime a string.
Mapeo de tipos simples con Mapster
En Mapster es todavía más sencillo:
UserDto destination = source.Adapt<UserDto>();
Podemos invocar directamente sobre el objeto de origen el método de extensión Adapt<TDestination>(this object source), al que debemos pasar el tipo de destino UserDto como parámetro de tipo genérico.
Pero si queremos mapear a un objeto de destino ya existente, podemos hacerlo así:
var destination = new UserDto(); source.Adapt(destination);
Mapster ofrece además otras formas, como query.ProjectToType<Dest>(), una instancia de IMapper para la inyección de dependencias y algunas más.
Ahora, si inspeccionamos los valores del objeto destination, vemos que el mapeo es el esperado:

Ebook gratis
¿Tu Web API está lista para producción?
33 puntos que comprobar antes de desplegarla, con la solución de cada uno. Un PDF gratuito de 76 páginas para .NET 10.
El ebook está en inglés.
Descarga la checklist gratisPDF gratuito. Un solo correo para enviártelo. Puedes darte de baja cuando quieras.
{"Id":1,"Name":"User 1","IsActive":true,"Email":"[email protected]","CreatedAt":"01-09-2022 21:53:57"}
Mapeo de tipos anidados
En este caso, tenemos que mapear objetos anidados. Para ilustrarlo, vamos a crear otro tipo llamado Address:
public class Address
{
public string AddressLine1 { get; set; } = null!;
public string AddressLine2 { get; set; } = null!;
public string City { get; set; } = null!;
public string State { get; set; } = null!;
public string Country { get; set; } = null!;
public string ZipCode { get; set; } = null!;
}
Vamos a usar el tipo Address como una de las propiedades de la clase User:
public class User
{
public int Id { get; set; }
public string FirstName { get; set; } = null!;
public string LastName { get; set; } = null!;
public string Email { get; set; } = null!;
public Address Address { get; set; } = null!;
}
En el mapeo de tipos anidados o complejos, las bibliotecas de mapeo tienen que mapear todos los tipos que encuentren a su paso, en cualquier nivel. En este caso, hay que mapear tanto User como Address a sus respectivos tipos de destino.
Supongamos que tenemos los tipos UserDto y AddressDto como destino.
Mapeo de tipos anidados con AutoMapper
En AutoMapper, tenemos que crear la configuración de mapeo de todos los tipos hacia sus tipos de destino:
var mapper = new MapperConfiguration(cfg =>
{
cfg.CreateMap<User, UserDto>();
cfg.CreateMap<Address, AddressDto>();
}, NullLoggerFactory.Instance)
.CreateMapper();
Aquí, hemos creado el mapeo de User y de Address a sus tipos de destino, UserDto y AddressDto.
Sin embargo, el mapeo en sí no cambia:
UserDto destination = mapper.Map<UserDto>(source);
Esta instrucción mapeará tanto el primer nivel (UserDto) como el segundo nivel dentro de UserDto (AddressDto).
Mapeo de tipos anidados con Mapster
En Mapster, como los tipos de origen y de destino tienen exactamente las mismas propiedades, no hay ningún cambio y basta con una instrucción sencilla:
UserDto destination = source.Adapt<UserDto>();
Mapeo de listas o arrays
En este caso, puede que queramos mapear una lista o un array a otra lista u otro array. Para ello, basta con indicar como parámetro de tipo de destino List<TDestination>, TDestination[] o algo similar. Aparte de eso, no necesitamos ninguna configuración especial para que funcione.
Mapeo de listas o arrays con AutoMapper
En AutoMapper, seguimos necesitando indicar la configuración de mapeo de los tipos individuales:
var mapper = new MapperConfiguration(cfg => cfg.CreateMap<User, UserDto>(), NullLoggerFactory.Instance).CreateMapper();
var sourceList = new List<User>() { ... };
List<UserDto> destinationList = mapper.Map<List<UserDto>>(sourceList);
Si nos fijamos, hemos pasado List<UserDto> como parámetro de tipo de destino al método Map<TDestination>().
Mapeo de listas o arrays con Mapster
En Mapster, podemos invocar directamente el método Adapt() y pasar List<UserDto> como parámetro de tipo de destino:
List<UserDto> destinationList = sourceList.Adapt<List<UserDto>>();
Mapeo personalizado de propiedades o miembros
En este caso, queremos mapear propiedades personalizadas entre los tipos de origen y de destino.
Supongamos que el tipo de destino tiene la propiedad FullName:
public class UserDto
{
public string FullName { get; set; } = null!;
}
Pero el tipo de origen solo tiene las propiedades FirstName y LastName:
public class User
{
public string FirstName { get; set; } = null!;
public string LastName { get; set; } = null!;
}
Aquí, la biblioteca de mapeo no sabrá qué propiedad mapear porque los nombres no coinciden. Por eso, tenemos que indicarle explícitamente nuestro mapeo personalizado. El mapeo personalizado también abarca el caso contrario: ignorar propiedades con AutoMapper o ignorar los valores null con AutoMapper para que un miembro de destino conserve su valor actual.
Mapeo personalizado de propiedades o miembros con AutoMapper
En AutoMapper, llamamos al método fluido ForMember, encadenado al método CreateMap(), para indicar el mapeo que queremos:
var mapper = new MapperConfiguration(cfg =>
{
cfg.CreateMap<User, UserDto>()
.ForMember(
dest => dest.FullName,
config => config.MapFrom(src => $"{src.FirstName} {src.LastName}"
));
}, NullLoggerFactory.Instance).CreateMapper();
Mapeo personalizado de propiedades o miembros con Mapster
En Mapster, podemos usar la clase estática TypeAdapterConfig para el mapeo personalizado de propiedades:
TypeAdapterConfig<User, UserDto>
.NewConfig()
.Map(dest => dest.FullName, src => $"{src.FirstName} {src.LastName}");
Aquí, tenemos que indicar el tipo de origen como primer parámetro de tipo y el tipo de destino como segundo parámetro de tipo. Por eso escribimos TypeAdapterConfig<User, UserDto>. El método fluido NewConfig() crea una nueva configuración de mapeo para los tipos de origen y de destino y descarta cualquier otra configuración que hubiera para esos tipos.
Aplanamiento de objetos
Con el aplanamiento de objetos (object flattening), podemos mapear las propiedades anidadas a propiedades de nivel superior mediante una convención de nombres sencilla.
Por ejemplo, la propiedad anidada Address.ZipCode del tipo de origen User se puede mapear a la propiedad AddressZipCode del tipo de destino UserDto:
public class User
{
public Address Address { get; set; } = null!;
}
public class Address
{
public string ZipCode { get; set; } = null!;
}
public class UserDto
{
public string AddressZipCode { get; set; } = null!;
}
Otra forma es tener un método con el prefijo Get seguido del nombre de la propiedad de destino. Por ejemplo, el método GetFullName() se mapeará a la propiedad FullName:
public class User
{
public string FirstName { get; set; } = null!;
public string LastName { get; set; } = null!;
public string GetFullName() => $"{FirstName} {LastName}";
}
public class UserDto
{
public string FullName { get; set; } = null!;
}
Podemos usar esta característica tanto en AutoMapper como en Mapster de forma predeterminada.
Mapeo inverso y desaplanamiento
El desaplanamiento de objetos (unflattening) es justo lo contrario. También podemos llamarlo mapeo inverso porque hacemos el mapeo del objeto de destino al de origen.
Mapeo inverso y desaplanamiento con AutoMapper
En AutoMapper, para conseguir tanto el mapeo inverso como el desaplanamiento, tenemos que llamar al método ReverseMap() en la configuración del mapeador:
var mapper = new MapperConfiguration(cfg => cfg.CreateMap<User, UserDto>().ReverseMap(), NullLoggerFactory.Instance).CreateMapper();
Mapeo inverso y desaplanamiento con Mapster
En Mapster, el mapeo inverso se denomina “two ways” (en ambos sentidos). Como sugiere el término, tenemos que llamar al método TwoWays() en la configuración del adaptador de tipos, aquí para un UserDto cuya propiedad EmailAddress se mapea desde Email:
TypeAdapterConfig<User, UserDto>
.NewConfig()
.TwoWays()
.Map(dest => dest.EmailAddress, src => src.Email);
Esto hará tanto el mapeo inverso como el desaplanamiento. Cualquier mapeo que siga al método TwoWays() se usará en ambos sentidos.
Mapeo con atributos
Hasta ahora, hemos visto la configuración fluida para distintos casos. También podemos usar atributos para conseguir la misma funcionalidad.
Tanto AutoMapper como Mapster ofrecen atributos para hacer el mapeo personalizado.
Mapeo con atributos en AutoMapper
En AutoMapper, podemos usar atributos como AutoMap, Ignore, SourceMember y otros más:
public class User
{
public DateTime CreatedAt { get; set; }
}
[AutoMap(typeof(User))]
public class UserDto
{
[SourceMember("CreatedAt")]
public string CreatedDate { get; set; } = null!;
}
Aquí hemos usado el atributo AutoMap en el tipo de destino para indicar el tipo de origen del mapeo. Y después, hemos usado el atributo SourceMember, del espacio de nombres AutoMapper.Configuration.Annotations, para vincular la propiedad de destino con la del tipo de origen.
Para que esto funcione, tenemos que usar en la configuración del mapeador el método AddMaps(), que recibe como parámetro el ensamblado de los tipos de origen y de destino:

Ebook gratis
¿Tu Web API está lista para producción?
33 puntos que comprobar antes de desplegarla, con la solución de cada uno. Un PDF gratuito de 76 páginas para .NET 10.
El ebook está en inglés.
Descarga la checklist gratisPDF gratuito. Un solo correo para enviártelo. Puedes darte de baja cuando quieras.
var mapper = new MapperConfiguration(cfg => cfg.AddMaps(typeof(User).Assembly), NullLoggerFactory.Instance).CreateMapper();
Cuando hacemos el mapeo, el resultado final es el mismo que con la configuración fluida.
Mapeo con atributos en Mapster
En Mapster, podemos usar atributos como AdaptTo, AdaptFrom, AdaptTwoWays, AdaptMember y algunos más para que hagan el mapeo por nosotros.
public class User
{
public DateTime CreatedAt { get; set; }
}
public class UserDto
{
[AdaptMember("CreatedAt")]
public string CreatedDate { get; set; } = null!;
}
En este caso, hemos usado el atributo AdaptMember para indicar la propiedad del tipo de origen.
Inyección de dependencias
Ambas bibliotecas admiten la inyección de dependencias (DI).
Inyección de dependencias con AutoMapper
Desde la versión 13, las extensiones de DI vienen incluidas en el paquete principal AutoMapper, así que ya no hay que instalar un paquete AutoMapper.Extensions.Microsoft.DependencyInjection aparte. Registramos todo con el método AddAutoMapper() sobre IServiceCollection:
builder.Services.AddAutoMapper(cfg =>
{
cfg.LicenseKey = builder.Configuration["AutoMapper:LicenseKey"];
}, typeof(Program));
Pasamos uno o varios tipos de marcador (aquí, Program) para que AutoMapper busque perfiles en sus ensamblados. Desde la versión 15.0.0, la modalidad comercial también espera una clave de licencia en tiempo de ejecución en producción, incluso en el nivel Community gratuito, que es una licencia y no la ausencia de una. La modalidad RPL-1.5 es la alternativa a tener una clave. Durante el desarrollo y las pruebas, AutoMapper funciona sin clave y solo registra un recordatorio.
Inyección de dependencias con Mapster
En Mapster, tenemos que instalar otro paquete NuGet, Mapster.DependencyInjection.
Y después tenemos que registrar el objeto TypeAdapterConfig como singleton y registrar la clase ServiceMapper para la interfaz IMapper, ambas del espacio de nombres MapsterMapper, con cualquier tiempo de vida:
builder.Services.AddSingleton(TypeAdapterConfig.GlobalSettings); builder.Services.AddScoped<IMapper, ServiceMapper>();
En ambas bibliotecas, podemos recibir la interfaz IMapper en el constructor para usar el objeto mapeador:
public class SampleService {
private readonly IMapper _mapper;
public SampleService(IMapper mapper) {
_mapper = mapper;
}
}
¿Es Mapster más rápido que AutoMapper?
Sí. En nuestras mediciones con BenchmarkDotNet en .NET 10, Mapster es más rápido que AutoMapper en todos los casos que probamos, aproximadamente entre 1.25 y 3.8 veces según el mapeo. La diferencia es menor en las copias simples de propiedades y en el mapeo de listas, donde no pasa de un 30 % aproximadamente, y mayor en los mapeos anidados, aplanados e inversos. La asignación de memoria, que antes era una clara ventaja de Mapster, ahora es prácticamente idéntica en las versiones actuales.
Estas cifras deben ir acompañadas de dos matices. Primero, el mapeo de objetos rara vez es el cuello de botella de una aplicación. Unos cientos de nanosegundos por objeto se vuelven insignificantes al lado de un solo viaje de ida y vuelta a la base de datos, así que la licencia y la ergonomía suelen pesar más que la velocidad.
Segundo, si el rendimiento es el criterio decisivo, los mapeadores basados en generadores de código fuente, como Mapperly, suelen superar a las dos bibliotecas que mapean en tiempo de ejecución. La documentación de Mapperly explica el mecanismo: “Como Mapperly crea el código de mapeo en tiempo de compilación, el costo adicional en tiempo de ejecución es mínimo”.
Para medir el rendimiento, vamos a usar la biblioteca BenchmarkDotNet, disponible para .NET.
Antes de seguir con las pruebas, veamos cómo hemos configurado el benchmark:
[Benchmark(Description = "AutoMapper_SimpleMapping")]
public void AutoMapperSimpleObjectMapping()
{
for (var i = 0; i < _size; i++)
{
AutoMapperSimpleTypeMapping.Map(_simpleObjectSource[i]);
}
}
El método AutoMapperSimpleObjectMapping() hará el mapeo de objetos para N elementos, según el valor del campo _size. En este caso, el método usa AutoMapper para un mapeo simple.
Veamos qué hace el método AutoMapperSimpleTypeMapping.Map():
public class AutoMapperSimpleTypeMapping
{
public static IMapper Mapper = new MapperConfiguration(cfg => cfg.CreateMap<User, UserDto>(), NullLoggerFactory.Instance).CreateMapper();
public static UserDto Map(User source)
{
var destination = Mapper.Map<UserDto>(source);
return destination;
}
}
El método Map() de la clase AutoMapperSimpleTypeMapping hace el mapeo de objeto a objeto propiamente dicho. Recibe el objeto de origen como parámetro y devuelve el objeto de destino después de hacer el mapeo. También hemos creado la instancia de IMapper necesaria para este caso de mapeo concreto.
Del mismo modo, hemos creado las clases y los métodos de cada caso tanto para AutoMapper como para Mapster. El código completo del benchmark, que usa los paquetes NuGet BenchmarkDotNet y Bogus, está en el repositorio de GitHub enlazado al principio de este artículo.
Veamos cómo generamos los objetos de origen:
public class SimpleTypeMappingDataGenerator
{
public static List<User> GetSources(int count = 1000)
{
var faker = new Faker<User>()
.Rules((f, o) =>
{
o.Id = f.Random.Number();
o.Name = f.Name.FullName();
o.Email = f.Person.Email;
o.IsActive = f.Random.Bool();
o.CreatedAt = DateTime.Now;
});
return faker.Generate(count);
}
}
El método GetSources() genera el número necesario de objetos de origen para el mapeo. Como el objeto de origen es distinto en cada caso, hemos creado clases generadoras de datos similares para cada uno.
Generamos los objetos de origen antes de ejecutar el benchmark propiamente dicho:
[GlobalSetup(Targets = new[] { nameof(AutoMapperSimpleObjectMapping), nameof(MapsterSimpleObjectMapping) })]
public void SetupDataSourceForSimpleTypeMapping()
{
_simpleObjectSource = SimpleTypeMappingDataGenerator.GetSources(_size).ToArray();
}
Por último, vamos a asignar al campo _size el valor 1000 y después ejecutar el benchmark en .NET 10 con las versiones actuales de las bibliotecas:
| Method | Mean | Error | StdDev | Allocated | |--------------------------------- |----------:|---------:|---------:|----------:| | AutoMapper_SimpleMapping | 219.02 us | 3.182 us | 2.977 us | 109.38 KB | | Mapster_SimpleMapping | 173.12 us | 3.410 us | 4.998 us | 109.38 KB | | AutoMapper_ListOrArrayMapping | 175.46 us | 2.646 us | 2.832 us | 133.45 KB | | Mapster_ListOrArrayMapping | 139.96 us | 2.786 us | 2.470 us | 125.11 KB | | AutoMapper_NestedMapping | 79.29 us | 0.876 us | 1.076 us | 117.19 KB | | Mapster_NestedMapping | 31.72 us | 0.632 us | 1.401 us | 117.19 KB | | AutoMapper_FlattenedMapping | 114.10 us | 2.153 us | 2.014 us | 136.84 KB | | Mapster_FlattenedMapping | 38.97 us | 0.768 us | 0.822 us | 136.63 KB | | AutoMapper_CustomPropertyMapping | 151.42 us | 1.289 us | 1.006 us | 89.84 KB | | Mapster_CustomPropertyMapping | 88.13 us | 1.272 us | 1.128 us | 89.69 KB | | AutoMapper_ReverseMapping | 109.45 us | 2.103 us | 2.582 us | 117.19 KB | | Mapster_ReverseMapping | 28.73 us | 0.261 us | 0.204 us | 117.19 KB | | AutoMapper_AttributeMapping | 243.54 us | 4.645 us | 4.562 us | 117.19 KB | | Mapster_AttributeMapping | 172.46 us | 2.290 us | 2.030 us | 117.19 KB |
Estas cifras proceden de BenchmarkDotNet v0.15.8 en .NET 10.0.10, con AutoMapper 16.2.0, Mapster 10.0.11 y Bogus 35.6.5 para generar los objetos de origen.
Los resultados muestran que Mapster es aproximadamente entre 1.25 y 3.8 veces más rápido que AutoMapper en estos casos, con la mayor ventaja en los mapeos anidados, aplanados e inversos. El consumo de memoria es ahora casi el mismo en ambas bibliotecas, así que la ventaja de Mapster aquí es la velocidad, no las asignaciones. Es un cambio respecto a versiones anteriores, en las que AutoMapper asignaba bastante más memoria.
En definitiva, vemos que Mapster es mejor opción cuando el rendimiento es crucial. Sin embargo, en aplicaciones en las que el rendimiento no es crítico, AutoMapper cumplirá perfectamente, y las condiciones de licencia deberían pesar al menos tanto como los microsegundos.
¿Cuáles son las mejores alternativas a AutoMapper?
Tres alternativas sirven para prácticamente cualquier migración desde AutoMapper. Mapster es el sustituto más directo: el mismo modelo de mapeo en tiempo de ejecución, con un conjunto de funcionalidades casi idéntico (reglas personalizadas por miembro, proyección con EF Core, compatibilidad con DI), pero más rápido y con licencia MIT. La mayoría de los equipos migra un par origen-destino en minutos: la configuración CreateMap pasa a ser TypeAdapterConfig, y mapper.Map<TDest>(src) pasa a ser src.Adapt<TDest>().
Mapperly es la opción en tiempo de compilación: un generador de código fuente de Roslyn que genera métodos de mapeo normales durante la compilación, lo que ofrece un costo adicional mínimo en tiempo de ejecución, depuración completa y, en lugar de sorpresas en tiempo de ejecución, advertencias y errores del compilador. El precio es declarar una clase parcial por cada mapeador.
La tercera alternativa es no usar ninguna biblioteca: los métodos de mapeo escritos a mano son explícitos, muy fáciles de probar y no dependen de nada, y por eso muchos equipos ven un cambio de licencia como el empujón para escribir a mano los mapeos de sus DTO. Lo que impulsa las tres es la doble licencia de AutoMapper desde la versión 15.0.0: las vías gratuitas son la modalidad recíproca RPL-1.5 o un nivel Community que exige cumplir a la vez sus cuatro condiciones.
| Criterio | AutoMapper | Mapster |
|---|---|---|
| Licencia | Doble licencia desde la 15.0.0 (julio de 2025): RPL-1.5 o comercial. El nivel Community es gratuito solo si se cumplen las cuatro condiciones: ingresos brutos anuales (o presupuesto, en organizaciones sin fines de lucro) inferiores a 5 millones de dólares, no más de 10 millones de dólares de capital externo, no ser una entidad gubernamental o cuasigubernamental y no ser una universidad que use la biblioteca para software institucional. Comercial desde 499 dólares al año para entre 1 y 10 desarrolladores. La 14.0.0 y anteriores, hasta la 2.0.0, siguen con MIT de forma permanente. | MIT, sin condiciones de elegibilidad: gratuito sea cual sea el tamaño de la empresa, sin ningún umbral que comprobar. |
| Rendimiento (nuestro benchmark) | Referencia | Aproximadamente entre 1.25 y 3.8 veces más rápido; asignación de memoria similar en las versiones actuales |
| Mapeo por convención sin configuración | No (sigue haciendo falta CreateMap para cada par) | Sí (Adapt() sin ninguna configuración) |
| Reglas personalizadas | CreateMap().ForMember(...) en perfiles | TypeAdapterConfig.NewConfig().Map(...) |
| Proyección con EF Core | ProjectTo<T>() | ProjectToType<T>() |
| Generación de código en tiempo de compilación | No | Sí (Mapster.Tool) |
| Registro en DI | AddAutoMapper() (integrado en el paquete principal desde la v13) | Paquete Mapster.DependencyInjection |
| Madurez / ecosistema | Desde 2009, la opción por defecto durante una década | Desde 2015, la principal alternativa gratuita |
Conclusión
En este artículo, hemos comparado el uso de AutoMapper y Mapster en algunos de los casos habituales de mapeo de objeto a objeto. Por último, hemos hecho un benchmark de rendimiento para averiguar cuál es el más eficiente. Los resultados del benchmark nos han mostrado que Mapster rinde mejor que AutoMapper en todos los casos. Además del rendimiento, Mapster también destaca en facilidad de uso, y su licencia MIT es una cosa menos que comprobar.
Si quieres conocer todas las opciones disponibles, consulta la documentación oficial de AutoMapper y de Mapster.
Probado con .NET 10.0.10, AutoMapper 16.2.0, Mapster 10.0.11 y BenchmarkDotNet 0.15.8.