O Mapster vence esta comparação na maioria dos projetos novos: é mais rápido em todos os casos que medimos no benchmark a seguir, exige menos configuração nos casos comuns e continua com licença MIT, sem nenhuma condição de elegibilidade.

O AutoMapper mudou de licença na versão 15.0.0, em julho de 2025. A mudança não foi para uma licença “comercial”, mas para uma licença dupla. Podemos adotar a Reciprocal Public License 1.5 e publicar nosso próprio código-fonte sob ela, ou adotar a licença comercial, que é gratuita no nível Community apenas se todas as quatro condições desse nível forem atendidas ao mesmo tempo. A versão 14.0.0 e todas as anteriores, até a 2.0.0, continuam sob MIT permanentemente.

O AutoMapper ainda tem seu lugar em bases de código que já o usam intensamente: é maduro, está documentado em toda parte, e a migração tem um custo real.

Veja como eles diferem, recurso por recurso, com medições.

Para baixar o código-fonte deste artigo, você pode acessar nosso repositório no GitHub.

Por que comparar AutoMapper vs Mapster?

O AutoMapper é uma das bibliotecas de mapeamento objeto a objeto mais populares, com mais de 1,1 bilhão de downloads do pacote NuGet. Foi publicado pela primeira vez em 2009, e seu uso cresce desde então.

Publicado pela primeira vez em 2015, o Mapster é uma alternativa ao AutoMapper e tem mais de 76 milhões de downloads do pacote NuGet. Embora fique muito atrás do AutoMapper em número de downloads, ele foi projetado para ser eficiente tanto em velocidade quanto em memória.

Pelo histórico de commits do AutoMapper e do Mapster, vemos que os dois projetos são mantidos ativamente.

Além disso, os dois oferecem opções de configuração para casos de mapeamento dos mais simples aos mais avançados.

A maior diferença entre os dois hoje é o licenciamento. A partir da versão 15.0.0, publicada em julho de 2025, o AutoMapper (junto com o MediatR, ambos mantidos pela Lucky Penny Software) tem licença dupla. Podemos aceitar a Reciprocal Public License 1.5, que é de código aberto e recíproca (ou seja, publicamos nosso próprio código-fonte sob ela e não pagamos nada), ou podemos adotar uma licença comercial. A versão 14.0.0 e todas as anteriores, até a 2.0.0, continuam sob MIT permanentemente, então fixar a versão em [14.0.0, 15.0.0) mantém um AutoMapper totalmente permissivo. O NuGet, porém, marca a 14.0.0 com um alerta de segurança de gravidade alta (CVE-2026-32933, uma vulnerabilidade de negação de serviço), e a correção saiu primeiro nas versões 15.1.1 e 16.1.1, já sob licença dupla. A Lucky Penny não fornece atualizações de segurança para as versões mais antigas sob MIT.

O lado comercial inclui um nível Community gratuito, mas apenas para pessoas físicas e organizações que atendem às quatro condições do fornecedor ao mesmo tempo: receita bruta anual (ou orçamento, no caso de organizações sem fins lucrativos) inferior a US$ 5 milhões, no máximo US$ 10 milhões em capital externo, não ser uma entidade governamental ou paragovernamental e não ser uma universidade que usa a biblioteca em software institucional ou operacional. Se uma única condição não for atendida, valem os níveis pagos: US$ 499, US$ 1.499 ou US$ 3.999 por ano, por produto, nas edições Standard, Professional e Enterprise.

O Mapster não tem nada disso. Ele continua com licença MIT, sem condições de elegibilidade, sem limite de receita e sem níveis, então é gratuito para empresas de qualquer porte. As condições e os preços do AutoMapper citados acima são os da própria Lucky Penny Software, tirados do seu FAQ de licenciamento e da sua página de preços, consultados em 13 de setembro de 2026.

Se quiser saber mais sobre essas bibliotecas, confira nossos artigos detalhados sobre o AutoMapper e o Mapster.

Para começar, vamos descobrir como o Mapster se compara ao AutoMapper em alguns dos casos mais comuns de mapeamento objeto a objeto.

Mapeamento de tipos simples

Neste caso, os tipos de origem e de destino têm propriedades semelhantes. Os nomes das propriedades são sempre os mesmos. No entanto, podemos usar tipos de dados diferentes nas propriedades. Nosso destino aqui é um DTO e, se você não tiver certeza de como ele se relaciona com um objeto simples, veja a diferença entre um DTO e um POCO.

Para demonstrar, vamos criar um tipo de origem simples, 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; }
}

Em seguida, vamos criar nosso 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!;
}

Aqui, todas as propriedades são semelhantes às do tipo de origem, exceto a propriedade CreatedAt. A propriedade CreatedAt tem string como tipo de destino, enquanto DateTime é o tipo de origem. Em situações como essa, as bibliotecas de mapeamento fazem a conversão de tipo automaticamente.

As duas bibliotecas oferecem suporte à conversão de tipo entre os tipos primitivos.

Mapeamento de tipos simples com o AutoMapper

Para usar o AutoMapper, primeiro precisamos criar o objeto IMapper. Há várias maneiras de criá-lo. Uma delas é usar a classe MapperConfiguration:

var mapper = new MapperConfiguration(cfg => cfg.CreateMap<User, UserDto>(), NullLoggerFactory.Instance).CreateMapper();

Desde a versão 15.0.0, o construtor de MapperConfiguration exige um ILoggerFactory. Aqui, passamos NullLoggerFactory.Instance, de Microsoft.Extensions.Logging.Abstractions, enquanto uma aplicação ASP.NET Core forneceria a fábrica por meio da injeção de dependência (DI). Os trechos de código deste artigo também precisam dos pacotes NuGet AutoMapper e Mapster e de diretivas using para os namespaces AutoMapper, Mapster e Microsoft.Extensions.Logging.Abstractions.

Dessa forma, precisamos especificar os tipos de origem e de destino como parâmetros de tipo do método CreateMap<TSource, TDestination>(). No nosso caso, User é o tipo de origem e UserDto é o tipo de destino.

A outra maneira de configurar o mapeador é usar perfis. Podemos saber mais sobre ela neste artigo.

Antes de continuar, vamos criar nosso objeto de origem com o tipo User:

var source = new User
{
    Id = 1,
    Name = "User 1",
    Email = "[email protected]",
    IsActive = true,
    CreatedAt = DateTime.Now
};

Com o objeto de origem e a instância IMapper prontos, podemos mapear facilmente para o objeto de destino:

UserDto destination = mapper.Map<UserDto>(source);

Neste caso, precisamos passar o objeto de origem como parâmetro e especificar o tipo de destino como parâmetro de tipo genérico do método Map<TDestination>(object source).

Se inspecionarmos os valores da variável destination, veremos que todos os valores das propriedades do tipo de origem foram mapeados corretamente para as propriedades do tipo de destino:

{"Id":1,"Name":"User 1","IsActive":true,"Email":"[email protected]","CreatedAt":"01-09-2022 21:53:57"}

A propriedade CreatedAt também é mapeada por conversão automática de tipo, de DateTime para string.

Mapeamento de tipos simples com o Mapster

No Mapster, é ainda mais simples:

UserDto destination = source.Adapt<UserDto>();

Podemos invocar diretamente o método de extensão Adapt<TDestination>(this object source) no objeto de origem, e nele devemos passar o tipo de destino UserDto como parâmetro de tipo genérico.

Mas, se quisermos mapear para um objeto de destino já existente, podemos fazer assim:

var destination = new UserDto();
source.Adapt(destination);

O Mapster também oferece outras formas, como query.ProjectToType<Dest>(), uma instância de IMapper para injeção de dependência e algumas outras.

Agora, se inspecionarmos os valores do objeto destination, veremos que o mapeamento sai como esperado:

The Web API Production Checklist, e-book gratuito em inglês

E-book gratuito

Sua Web API está pronta para produção?

33 itens para verificar antes de implantá-la, com a correção de cada um. Um PDF gratuito de 76 páginas para .NET 10.

O e-book está em inglês.

Baixe o checklist gratuito

PDF gratuito. Um único e-mail para enviá-lo. Cancele a inscrição quando quiser.

{"Id":1,"Name":"User 1","IsActive":true,"Email":"[email protected]","CreatedAt":"01-09-2022 21:53:57"}

Mapeamento de tipos aninhados

Neste caso, precisamos mapear objetos aninhados. Para ilustrar, vamos criar mais um tipo, chamado 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 usar o tipo Address como uma das propriedades da classe 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!;
}

No mapeamento de tipos aninhados ou complexos, as bibliotecas de mapeamento precisam mapear todos os tipos que encontram pelo caminho, em qualquer nível. Aqui, é preciso mapear tanto User quanto Address para os respectivos tipos de destino.

Vamos supor que tenhamos os tipos UserDto e AddressDto como destino.

Mapeamento de tipos aninhados com o AutoMapper

No AutoMapper, precisamos criar a configuração de mapeamento de todos os tipos para os respectivos tipos de destino:

var mapper = new MapperConfiguration(cfg =>
    {
        cfg.CreateMap<User, UserDto>();
        cfg.CreateMap<Address, AddressDto>();
    }, NullLoggerFactory.Instance)
    .CreateMapper();

Aqui, criamos o mapeamento de User e de Address para os tipos de destino UserDto e AddressDto.

No entanto, o mapeamento em si não muda:

UserDto destination = mapper.Map<UserDto>(source);

Esse comando mapeia tanto o primeiro nível (UserDto) quanto o segundo nível, dentro de UserDto (AddressDto).

Mapeamento de tipos aninhados com o Mapster

No Mapster, como os tipos de origem e de destino têm exatamente as mesmas propriedades, nada muda, e um comando simples basta:

UserDto destination = source.Adapt<UserDto>();

Mapeamento de listas ou arrays

Neste caso, talvez queiramos mapear uma lista ou um array para outro. Para isso, basta especificar o parâmetro de tipo de destino como List<TDestination>, TDestination[] ou algo semelhante. Fora isso, não precisamos de nenhuma configuração especial para que funcione.

Mapeamento de listas ou arrays com o AutoMapper

No AutoMapper, ainda precisamos especificar a configuração de mapeamento para os tipos individuais:

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

Se olharmos com atenção, veremos que fornecemos List<UserDto> como parâmetro de tipo de destino do método Map<TDestination>().

Mapeamento de listas ou arrays com o Mapster

No Mapster, podemos invocar diretamente o método Adapt(), fornecendo List<UserDto> como parâmetro de tipo de destino:

List<UserDto> destinationList = sourceList.Adapt<List<UserDto>>();

Mapeamento personalizado de propriedades ou membros

Neste caso, queremos mapear nossas propriedades personalizadas entre os tipos de origem e de destino.

Digamos que nosso tipo de destino tenha a propriedade FullName:

public class UserDto
{
    public string FullName { get; set; } = null!;
}

Mas nosso tipo de origem só tem as propriedades FirstName e LastName:

public class User
{
    public string FirstName { get; set; } = null!;
    public string LastName { get; set; } = null!;
}

Agora, a biblioteca de mapeamento não vai saber qual propriedade mapear, porque os nomes não coincidem. Por isso, precisamos informar explicitamente nosso mapeamento personalizado à biblioteca. O mapeamento personalizado também cobre o caso oposto: dizer ao AutoMapper para ignorar propriedades ou para ignorar valores null, de modo que um membro de destino mantenha o valor que já tem.

Mapeamento personalizado de propriedades ou membros com o AutoMapper

No AutoMapper, chamamos o método fluente ForMember encadeado ao método CreateMap() para especificar o mapeamento pretendido:

var mapper = new MapperConfiguration(cfg =>
{
    cfg.CreateMap<User, UserDto>()
        .ForMember(
            dest => dest.FullName,
            config => config.MapFrom(src => $"{src.FirstName} {src.LastName}"
            ));
}, NullLoggerFactory.Instance).CreateMapper();

Mapeamento personalizado de propriedades ou membros com o Mapster

No Mapster, podemos usar a classe estática TypeAdapterConfig para fazer o mapeamento personalizado de propriedades:

TypeAdapterConfig<User, UserDto>
    .NewConfig()
    .Map(dest => dest.FullName, src => $"{src.FirstName} {src.LastName}");

Aqui, precisamos especificar o tipo de origem como primeiro parâmetro de tipo e o tipo de destino como segundo parâmetro de tipo. Por isso, escrevemos TypeAdapterConfig<User, UserDto>. O método fluente NewConfig() cria uma nova configuração de mapeamento para os tipos de origem e de destino, descartando qualquer outra configuração existente para esses tipos.

Achatamento de objetos (flattening)

No achatamento de objetos, podemos mapear as propriedades aninhadas para propriedades de nível superior usando uma convenção de nomes simples.

Por exemplo, a propriedade aninhada Address.ZipCode do tipo de origem User pode ser mapeada para a propriedade AddressZipCode do 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!;
}

Outra forma é ter um método com o prefixo Get seguido do nome da propriedade de destino. Por exemplo, o método GetFullName() será mapeado para a propriedade 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 esse recurso por padrão tanto no AutoMapper quanto no Mapster.

Mapeamento reverso e desachatamento (unflattening)

O desachatamento de objetos é exatamente o oposto. Também podemos chamá-lo de mapeamento reverso porque fazemos o mapeamento do objeto de destino para o de origem.

Mapeamento reverso e desachatamento com o AutoMapper

No AutoMapper, para fazer o mapeamento reverso e o desachatamento, precisamos chamar o método ReverseMap() na configuração do mapeador:

var mapper = new MapperConfiguration(cfg => cfg.CreateMap<User, UserDto>().ReverseMap(), NullLoggerFactory.Instance).CreateMapper();

Mapeamento reverso e desachatamento com o Mapster

No Mapster, o mapeamento reverso é chamado de “two ways”. Como o termo sugere, precisamos chamar o método TwoWays() na configuração do adaptador de tipos, aqui para um UserDto cuja propriedade EmailAddress é mapeada a partir de Email:

TypeAdapterConfig<User, UserDto>
    .NewConfig()
    .TwoWays()
    .Map(dest => dest.EmailAddress, src => src.Email);

Isso faz tanto o mapeamento reverso quanto o desachatamento. Qualquer mapeamento que venha depois do método TwoWays() será usado nas duas direções.

Mapeamento com atributos

Até agora, vimos a configuração fluente para diferentes casos. Também podemos usar atributos para obter a mesma funcionalidade.

Tanto o AutoMapper quanto o Mapster oferecem atributos para fazer o mapeamento personalizado.

Mapeamento com atributos no AutoMapper

No AutoMapper, podemos usar atributos como AutoMap, Ignore, SourceMember e outros:

public class User
{
    public DateTime CreatedAt { get; set; }
}

[AutoMap(typeof(User))]
public class UserDto
{
    [SourceMember("CreatedAt")]
    public string CreatedDate { get; set; } = null!;
}

Aqui, usamos o atributo AutoMap no tipo de destino para especificar o tipo de origem do mapeamento. Em seguida, usamos o atributo SourceMember, do namespace AutoMapper.Configuration.Annotations, para mapear para a propriedade do tipo de origem.

Para que isso funcione, precisamos usar na configuração do mapeador o método AddMaps(), que recebe como parâmetro o assembly dos tipos de origem e de destino:

The Web API Production Checklist, e-book gratuito em inglês

E-book gratuito

Sua Web API está pronta para produção?

33 itens para verificar antes de implantá-la, com a correção de cada um. Um PDF gratuito de 76 páginas para .NET 10.

O e-book está em inglês.

Baixe o checklist gratuito

PDF gratuito. Um único e-mail para enviá-lo. Cancele a inscrição quando quiser.

var mapper = new MapperConfiguration(cfg => cfg.AddMaps(typeof(User).Assembly), NullLoggerFactory.Instance).CreateMapper();

Ao executar o mapeamento, o resultado final será o mesmo da configuração fluente.

Mapeamento com atributos no Mapster

No Mapster, podemos usar atributos como AdaptTo, AdaptFrom, AdaptTwoWays, AdaptMember e outros para fazer o mapeamento por nós.

public class User
{
    public DateTime CreatedAt { get; set; }
}

public class UserDto
{
    [AdaptMember("CreatedAt")]
    public string CreatedDate { get; set; } = null!;
}

Neste caso, usamos o atributo AdaptMember para especificar a propriedade do tipo de origem.

Injeção de dependência

As duas bibliotecas oferecem suporte à injeção de dependência.

Injeção de dependência com o AutoMapper

Desde a versão 13, as extensões de DI vêm dentro do pacote principal AutoMapper, então não há mais um pacote AutoMapper.Extensions.Microsoft.DependencyInjection separado para instalar. Registramos tudo com o método AddAutoMapper() em IServiceCollection:

builder.Services.AddAutoMapper(cfg =>
{
    cfg.LicenseKey = builder.Configuration["AutoMapper:LicenseKey"];
}, typeof(Program));

Passamos um ou mais tipos marcadores (aqui, Program) para que o AutoMapper procure perfis nos assemblies deles. A partir da versão 15.0.0, a opção comercial também exige uma chave de licença em tempo de execução em produção, inclusive no nível Community gratuito, que é uma licença, e não a ausência de uma. A opção RPL-1.5 é a alternativa a ter uma chave. Durante o desenvolvimento e os testes, o AutoMapper funciona sem chave e apenas registra um lembrete no log.

Injeção de dependência com o Mapster

No Mapster, precisamos instalar outro pacote NuGet, o Mapster.DependencyInjection.

Em seguida, precisamos registrar o objeto TypeAdapterConfig como singleton e registrar a classe ServiceMapper para a interface IMapper, ambas do namespace MapsterMapper, com qualquer tempo de vida:

builder.Services.AddSingleton(TypeAdapterConfig.GlobalSettings);
builder.Services.AddScoped<IMapper, ServiceMapper>();

Nas duas bibliotecas, podemos receber a interface IMapper no construtor para usar o objeto mapeador:

public class SampleService {
    private readonly IMapper _mapper;

    public SampleService(IMapper mapper) {
        _mapper = mapper;
    }
}

O Mapster é mais rápido que o AutoMapper?

Sim. Nas nossas medições com o BenchmarkDotNet no .NET 10, o Mapster é mais rápido que o AutoMapper em todos os casos que testamos, por uma margem de aproximadamente 1,25× a 3,8×, dependendo do mapeamento. A diferença é menor nas cópias simples de propriedades e no mapeamento de listas, em que os dois ficam a cerca de 30% um do outro, e maior nos mapeamentos aninhados, achatados e reversos. A alocação de memória, que já foi uma vantagem clara do Mapster, agora é praticamente idêntica nas versões atuais.

Duas ressalvas devem acompanhar os números. Primeiro, o mapeamento de objetos raramente é o gargalo de uma aplicação. Algumas centenas de nanossegundos por objeto desaparecem perto de uma única ida e volta ao banco de dados, então o licenciamento e a ergonomia costumam pesar mais que a velocidade.

Segundo, se o desempenho for o critério decisivo, mapeadores que usam geradores de código-fonte, como o Mapperly, costumam superar as duas bibliotecas de tempo de execução. A documentação do Mapperly explica o mecanismo: “Como o Mapperly cria o código de mapeamento em tempo de compilação, o custo adicional em tempo de execução é mínimo”.

Para medir o desempenho, vamos usar a biblioteca BenchmarkDotNet, disponível para o .NET.

Antes de prosseguir com os testes, vamos dar uma olhada na configuração do nosso benchmark:

[Benchmark(Description = "AutoMapper_SimpleMapping")]
public void AutoMapperSimpleObjectMapping()
{
    for (var i = 0; i < _size; i++)
    {
        AutoMapperSimpleTypeMapping.Map(_simpleObjectSource[i]);
    }
}

O método AutoMapperSimpleObjectMapping() faz o mapeamento de objetos para N itens, com base no campo _size. Aqui, o método faz o mapeamento com o AutoMapper em um caso de mapeamento simples.

Vamos descobrir o que o método AutoMapperSimpleTypeMapping.Map() faz:

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

O método Map() da classe AutoMapperSimpleTypeMapping faz o mapeamento objeto a objeto propriamente dito. Ele recebe o objeto de origem como parâmetro e retorna o objeto de destino depois de fazer o mapeamento. Também criamos a instância IMapper necessária para esse caso de mapeamento específico.

Da mesma forma, criamos as classes e os métodos de cada caso, tanto para o AutoMapper quanto para o Mapster. O código completo do benchmark, que usa os pacotes NuGet BenchmarkDotNet e Bogus, está no repositório do GitHub cujo link aparece no início deste artigo.

Vejamos como geramos os objetos de origem:

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

O método GetSources() produz o número necessário de objetos de origem para o mapeamento. Como o objeto de origem muda em cada caso, criamos classes geradoras de dados semelhantes para cada um deles.

Geramos os objetos de origem antes de executar o benchmark propriamente dito:

[GlobalSetup(Targets = new[] { nameof(AutoMapperSimpleObjectMapping), nameof(MapsterSimpleObjectMapping) })]
public void SetupDataSourceForSimpleTypeMapping()
{
    _simpleObjectSource = SimpleTypeMappingDataGenerator.GetSources(_size).ToArray();
}

Por fim, vamos definir o valor do campo _size como 1.000 e executar o benchmark no .NET 10 com as versões atuais das 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 |

Esses números vêm do BenchmarkDotNet v0.15.8 no .NET 10.0.10, com o AutoMapper 16.2.0, o Mapster 10.0.11 e o Bogus 35.6.5 gerando os objetos de origem.

Pelos resultados, vemos que o Mapster é cerca de 1,25 a 3,8 vezes mais rápido que o AutoMapper nesses casos, com a maior vantagem nos mapeamentos aninhado, achatado e reverso. O consumo de memória agora é quase o mesmo nas duas bibliotecas, então a vantagem do Mapster aqui é a velocidade, e não as alocações. Isso mudou em relação às versões anteriores, em que o AutoMapper alocava visivelmente mais.

No fim, concluímos que o Mapster é a melhor opção quando o desempenho é crucial. Em aplicações em que o desempenho não é crítico, porém, o AutoMapper resolve muito bem, e os termos de licenciamento devem pesar pelo menos tanto quanto os microssegundos.

Quais são as melhores alternativas ao AutoMapper?

Três alternativas cobrem praticamente toda migração que deixa o AutoMapper para trás. O Mapster é o substituto mais direto: o mesmo modelo de mapeamento em tempo de execução, com um conjunto de recursos quase idêntico (regras personalizadas por membro, projeção no EF Core, suporte a DI), só que mais rápido e com licença MIT. A maioria das equipes migra um par de tipos em minutos: a configuração CreateMap vira TypeAdapterConfig, e mapper.Map<TDest>(src) vira src.Adapt<TDest>().

O Mapperly é a opção em tempo de compilação: um gerador de código-fonte do Roslyn que emite métodos de mapeamento simples durante a compilação, o que resulta em custo adicional mínimo em tempo de execução, depuração completa e, em vez de surpresas em tempo de execução, avisos e erros do compilador. O preço é declarar uma classe parcial para cada mapeador.

A terceira alternativa é não usar biblioteca nenhuma: métodos de mapeamento escritos à mão são explícitos, fáceis de testar e livres de qualquer dependência, e é por isso que muitas equipes veem uma mudança de licença como o empurrão para escrever à mão os mapeamentos dos seus DTOs. O que motiva as três é a licença dupla do AutoMapper a partir da versão 15.0.0: os caminhos gratuitos são a opção recíproca RPL-1.5 ou um nível Community que exige as quatro condições ao mesmo tempo.

CritérioAutoMapperMapster
LicençaLicença dupla a partir da 15.0.0 (julho de 2025): RPL-1.5 ou comercial. O nível Community é gratuito apenas se todas as quatro condições forem atendidas: receita bruta anual (ou orçamento, no caso de organizações sem fins lucrativos) inferior a US$ 5 milhões, no máximo US$ 10 milhões de capital externo, não ser uma entidade governamental ou paragovernamental e não ser uma universidade que o usa em software institucional. Comercial a partir de US$ 499/ano para 1 a 10 desenvolvedores. A 14.0.0 e as anteriores, até a 2.0.0, continuam sob MIT permanentemente.MIT, sem condições de elegibilidade: gratuito para empresas de qualquer porte, sem nenhum limite a verificar.
Desempenho (nosso benchmark)Referência~1,25 a 3,8× mais rápido; alocação de memória semelhante nas versões atuais
Mapeamento por convenção sem configuraçãoNão (CreateMap ainda é necessário para cada par)Sim (Adapt() sem nenhuma configuração)
Regras personalizadasCreateMap().ForMember(...) em perfisTypeAdapterConfig.NewConfig().Map(...)
Projeção no EF CoreProjectTo<T>()ProjectToType<T>()
Geração de código em tempo de compilaçãoNãoSim (Mapster.Tool)
Registro na DIAddAutoMapper() (incluído no pacote principal desde a v13)Pacote Mapster.DependencyInjection
Maturidade / ecossistemaDesde 2009, o padrão por uma décadaDesde 2015, a principal alternativa gratuita

Conclusão

Neste artigo, comparamos o uso do AutoMapper e do Mapster em alguns dos casos mais comuns de mapeamento objeto a objeto. Por fim, fizemos um benchmark de desempenho para descobrir qual é a melhor opção. Pelo resultado do benchmark, vimos que o Mapster tem desempenho melhor que o AutoMapper em todos os casos. Além do desempenho, o Mapster se destaca pela facilidade de uso, e sua licença MIT é uma coisa a menos para verificar.

Se quiser conhecer todas as opções disponíveis, confira a documentação oficial do AutoMapper e do Mapster.

Testado com .NET 10.0.10, AutoMapper 16.2.0, Mapster 10.0.11 e BenchmarkDotNet 0.15.8.