AutoMapper has long been one of the go-to libraries for object-to-object mapping in .NET applications. It lets you transfer data between models (e.g. domain entities to DTOs) without writing manual property assignments. However, with AutoMapper's recent announcement of a move toward a paid licensing model, developers and teams are reconsidering their mapping strategy.
In this context, Mapster emerges as a compelling, free, open-source alternative. It provides a flexible, efficient, and modern approach to object mapping in .NET. In this article, you’ll learn what Mapster is, how to get started with it, how to migrate from AutoMapper, and best practices for using it in your projects.
What Is Mapster?
Mapster is an open-source object mapping library for .NET designed to combine the simplicity of convention-based mappers with performance and configurability. It supports both runtime mapping and compile-time code generation, giving you flexibility for different use cases.
Some of Mapster's key strengths:
- High performance: Mapster is designed to be fast and memory-efficient. Experiments and benchmarks show that its mapping operations can outperform other mapping libraries under many scenarios.
- Flexible modes (runtime + compile-time): By default, Mapster can use runtime mapping (via expression trees and compiled delegates). But with its tooling (Mapster.Tool, source generation), you can generate mapping code at compile time, which helps reduce runtime overhead and helps with debugging and validation.
- Convention-based simplicity with customization: Mapster will map properties with matching names and compatible types automatically. You can also define custom mappings for properties with different names, flattening, nested mappings, conditional logic, and more.
- Debuggability: Because Mapster can generate real mapping code, you can step into mapping logic in a debugger to inspect what's happening.
- Projection support: It can integrate well with LINQ/Entity Framework query projections, enabling efficient database queries that directly map to DTO shapes.
In short, Mapster offers a balance of developer ergonomics and runtime performance, making it a very viable replacement for AutoMapper in many projects.
Getting Started with Mapster
Here's a step-by-step guide to using Mapster in a .NET project:
1. Install NuGet Packages
In your project, add the following:
dotnet add package Mapster
dotnet add package Mapster.DependencyInjection
If you plan to use source-generation (compile-time mapping), also add:
dotnet add package Mapster.Tool
2. Define Your Models / DTOs
Define your domain models and your DTOs (or whatever mapping targets you have). For example:
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
public class ProductDto
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
With such matching properties, Mapster can map automatically.
3. Basic Mapping with Adapt<T>
Mapster provides the Adapt<TDestination>() extension method. It's the simplest way to convert an object:
Product product = new Product { Id = 1, Name = "Laptop", Price = 999.99m };
ProductDto dto = product.Adapt<ProductDto>();
This works without any configuration for types with matching property names and compatible types.
4. Custom Mapping Configuration
Often, source and destination objects don’t have perfectly matching property names or you need transformations. Mapster supports a configuration API:
var config = TypeAdapterConfig.GlobalSettings;
config.NewConfig<Product, ProductDto>()
.Map(dest => dest.Name, src => src.Name.ToUpper())
.Ignore(dest => dest.SomeOtherProperty);
You can also group configurations, scan an assembly, or use more advanced mapping features.
5. Register in Dependency Injection (ASP.NET Core / DI)
If you're using ASP.NET Core, you can integrate Mapster easily:
builder.Services.AddMapster(); // from Mapster.DependencyInjection
// Optionally scan for mapping registrations
TypeAdapterConfig.GlobalSettings.Scan(Assembly.GetExecutingAssembly());
Then in your services, you can inject an IMapper abstraction provided by Mapster or simply call Adapt<>() directly.
Example:
public class ProductService
{
private readonly IMapper _mapper; // Mapster's mapper interface
public ProductService(IMapper mapper)
{
_mapper = mapper;
}
public ProductDto GetDto(Product entity)
{
return _mapper.Map<ProductDto>(entity);
}
}
Alternatively:
public ProductDto GetDto(Product entity)
{
return entity.Adapt<ProductDto>();
}
6. Enabling Compile-Time Code Generation (Optional but Recommended)
For the best performance and compile-time safety, use Mapster’s source generation. When you include the Mapster.Tool package, you can annotate your classes or mappings to generate mapping code at build time.
Example with attributes:
[AdaptTo(typeof(ProductDto))]
public partial class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
This instructs Mapster to generate static mapping methods in a partial class. You can then call, e.g.:
var dto = product.AdaptToDto();
And under the hood, it will use the generated code rather than runtime reflection/expressions.
Compile-time generation gives you:
- Better performance: no runtime overhead for mapping setup.
- Debuggability: you can step into the generated mapping code.
- Early errors: if a mapping is missing or misconfigured, you may catch it at compile time rather than runtime.
Migrating from AutoMapper to Mapster
If you have an existing project that uses AutoMapper, here's how you can approach migration:
- Keep both libraries side-by-side initially. Don’t remove AutoMapper all at once. Gradually switch mapping calls.
- Replace mapping calls: Where you currently call mapper.Map<TDest>(source), replace (or overload) with source.Adapt<TDest>() or _mapper.Map<TDest>(source) using Mapster's IMapper.
- Convert AutoMapper profiles: For each CreateMap<Source, Dest> and custom ForMember(...) rules, translate them into equivalent Mapster configuration (e.g. NewConfig<Source, Dest>()).
- Scan for profiles: You can scan your assemblies for mapping configurations to register them automatically.
- Write tests: Add unit tests to validate that mapped logic behaves the same as before, especially for edge cases.
- Enable compile-time generation selectively: For mappings that are performance-critical, convert them to use the Mapster code-generation approach.
- Remove AutoMapper: Once you're confident all mappings have been migrated and work correctly, you can remove your AutoMapper dependency.
Because Mapster supports both automatic conventions and explicit configuration, the conversion from AutoMapper is often straightforward. In many cases, only the configuration style changes, while the core mapping logic remains conceptually the same.
Best Practices & Tips
- Favor compile-time mapping (code generation) for critical or hot paths — it gives performance benefits and safety.
- Use TypeAdapterConfig for organizing and centralizing mapping rules rather than scattering configuration inline.
- Be careful with nested objects, collections, and nullability — always test edge cases.
- For complex transformations, use Map(...) lambdas or AfterMapping / BeforeMapping hooks to insert custom logic.
- Leverage projection support when mapping from database queries (e.g. EF Core) so you don’t fetch entire entities, only required fields.
- Keep mapping logic thin and avoid embedding business logic in your mapping configurations.
- Use naming conventions and consistent DTO patterns to reduce the need for special configuration.
- Profile mapping performance if mapping large object graphs or doing bulk mapping — sometimes a custom or manual mapping may outperform abstraction for specific hot paths.
Conclusion
AutoMapper's shift to a paid licensing model has prompted developers to reconsider their mapping strategies. For those seeking a robust, open-source solution, Mapster provides a compelling alternative. It combines convention-based ease, high performance, and optional compile-time generation, making it a suitable choice for many .NET projects.
By gradually migrating your mappings, adopting Mapster's configuration API, and optionally enabling source generation, you can transition smoothly away from AutoMapper — gaining better performance, transparency, and freedom in the process.