Skip to content

Why Mapsicle

Arnel Robles edited this page Sep 28, 2026 · 1 revision

Why Choose Mapsicle?

AutoMapper 15.0 and later are no longer permissively licensed. They are governed by RPL-1.5 or a licence agreement from Lucky Penny Software, which includes a free Community License for those who qualify. Earlier versions keep their original licence. RPL-1.5 is strong reciprocal: its source obligations reach software that is only deployed internally, not just software you distribute. Check the terms against your own situation rather than taking this paragraph as advice. Mapsicle is MIT, the same licence as Mapperly and Mapster, so moving to it does not trade one licence question for another.

When Mapsicle is the right choice, and when it is not

This section used to open by saying Mapsicle loses to a source generator on speed and always will. That stopped being true in 2.2.0: declare a pair and it is measured at 1.00x hand written code on a nine type aggregate, against 1.08 for Mapperly and 1.09 for Mapster. Undeclared pairs are a different story and are still 1.80x.

Be honest about the size of that lead. Ten percent against two mappers that are also free and also MIT is a tiebreaker, not a migration. What is worth switching for is that you get it without configuring anything, and that an undeclared pair still maps instead of failing to compile.

Reach for Mapsicle when:

Situation Why the alternatives do not fit
Mapping a Dictionary<string, object> into a type Mapperly has nothing to generate against. AutoMapper needs the pair configured.
A collection whose items have different runtime types Same: nothing to generate, and the shape is only known at runtime.
Types arriving from plugins, reflection or configuration Compile-time generation is not available at all.
Hundreds of DTOs and no appetite for a CreateMap per pair Mapsicle maps by convention with no setup. AutoMapper throws when it reaches an unconfigured pair. AssertConfigurationIsValid() validates the maps you configured, so it does not catch a pair you never registered.
Object graphs that contain cycles Mapsicle returns the default at MaxDepth with no configuration. AutoMapper needs PreserveReferences() or MaxDepth(...), and an unhandled cycle can still overflow the stack. Mapperly needs UseReferenceHandling and Mapster needs PreserveReference; on default settings both abort the process.
The licence has to be permissive Only AutoMapper is a problem here. Mapperly and Mapster are MIT too, so this rules out one of the four rather than picking Mapsicle.

Reach for something else when:

Situation Choose
The compiler must prove every pair maps Mapperly. A pair it cannot generate does not compile. Mapsicle warns with MSG001 and falls back, which is safer at run time and weaker as a guarantee.
A rename must never silently unmap a member Mapperly. Its [MapProperty] uses nameof, so a rename is a compile error. Mapsicle's [MapFrom] takes a string and quietly stops matching.
You need AOT with no path that can fall back to runtime code generation Mapperly. Mapsicle's declared pairs are AOT clean, but an undeclared one throws NotSupportedException at first use under NativeAOT, and nothing at build time stops you leaving one undeclared by accident.
Collection throughput at around a hundred elements bounds your workload Mapperly, by about 12 percent over an undeclared Mapsicle pair on x64. A declared pair closes that.
You want AutoMapper's configuration API without AutoMapper's licence Mapster. Its fluent config is deliberately shaped like CreateMap, so porting is mechanical. Mapsicle's fluent API is its own shape.

Mapsicle is 1.30x to 1.44x faster than AutoMapper on single objects depending on the architecture, 1.09x to 1.20x on collections, and faster again on deeply nested graphs and large collections. Against Mapperly and Mapster the gap is ten percent either way. All of that is measured below, and all of it is a supporting argument rather than the reason to switch.

Quick Comparison

Feature Mapsicle AutoMapper Mapperly Mapster
License MIT RPL-1.5, or a Lucky Penny Software agreement (a free Community License exists) MIT MIT
Architecture Runtime + Caching, with an opt-in source generator Runtime + Expressions Source Generator Runtime + Expressions, with an optional codegen tool
Setup Required None, or one line to bind the assembly at compile time Profiles, DI Partial class None
Dependencies 0 (core) 8 1, attributes only 1 (Mapster.Core)
Deployed size 59.5 KB 1,117.4 KB 29.5 KB, attributes only 198.0 KB
Warm map, measured 1.00x hand written when the pair is declared, 1.80x when it is not 2.48x 1.08x 1.09x
Compile-time Safety Partial. A pair it cannot emit warns and falls back No Full. It will not compile Partial
AOT Compatible Declared pairs yes, undeclared no No Yes, with no fallback to get wrong With its codegen tool
Circular Refs Terminates and returns a usable object, with no configuration. The cycle is expanded to MaxDepth (32) copies before a repeated instance stops it Preserves the reference by default (measured on 15.1.3 with a plain CreateMap) Default settings overflow the stack; correct with UseReferenceHandling Default settings overflow the stack; correct with PreserveReference
Memory Bounded LRU Option No N/A No
Cache Statistics Yes No N/A No
Integrated Validation Yes No No No
ASP.NET Core Helpers Yes No No No

Size

Two projects, each referencing one mapper and nothing else, dotnet publish -c Release on net8.0:

Mapsicle AutoMapper 15.1.3 Mapperly 4.1.1 Mapster 7.4.0
the mapper's own assembly 59.5 KB 286.0 KB 29.5 KB, attributes only 166.5 KB
assemblies it brings with it 0 8 0 1
total on disk 59.5 KB 1,117.4 KB 29.5 KB 198.0 KB

Mapperly wins this one, and for a real reason: the only thing it ships at runtime is the attributes assembly, because the mapping itself is your own source. Mapsicle is second and Mapster deploys Mapster.Core alongside it.

More than half of what AutoMapper 15 deploys is not mapping code. Microsoft.IdentityModel.Tokens, JsonWebTokens, Logging and Abstractions come to 599.1 KB, and they are there because AutoMapper 15 validates a signed licence key. Referencing it puts a JWT validation stack into your dependency closure, larger than the mapper itself, to check that you are allowed to use the mapper. The remaining 232.4 KB is Microsoft.Extensions.* for dependency injection, options and logging.

Mapsicle's core has none of that, and not by luck: the core-has-no-dependencies job packs src/Mapsicle and fails if the nuspec declares a single dependency. Everything else in the ecosystem is a separate opt-in package.


Detailed Comparison: Mapsicle vs AutoMapper vs Mapperly vs Mapster

Core Mapping Features

Feature Mapsicle AutoMapper Mapperly
Convention-based mapping ✅ ✅ ✅
Flattening (Address.City to AddressCity) ✅ ✅ ✅
Custom member mapping ✅ ForMember() ✅ ForMember() ✅ [MapProperty]
Ignore members ✅ [IgnoreMap] ✅ Ignore() ✅ [MapperIgnore]
Reverse mapping ✅ ReverseMap() ✅ ReverseMap() ✅ (define both)
Before/After map hooks ✅ ✅ ✅
Type converters ✅ CreateConverter<>() ✅ ConvertUsing() ✅ User methods
Inheritance/Polymorphism ✅ Include<>() ✅ Include<>() ✅
Nested object mapping ✅ ✅ ✅
Collection mapping ✅ ✅ ✅
Constructor mapping ✅ ConstructUsing() ✅ ConstructUsing() ✅ (automatic)

Configuration & Organization

Feature Mapsicle AutoMapper Mapperly
Profile support ✅ MapsicleProfile ✅ Profile ❌ (partial classes)
Fluent configuration ✅ ✅ ❌ (attributes)
Attribute-based config ✅ [MapFrom] ✅ ✅
Static zero-config API ✅ obj.MapTo<T>() ❌ ❌
DI-friendly ✅ IMapper ✅ IMapper ✅
Assembly scanning ✅ ✅ N/A

Extension Packages

Package/Feature Mapsicle AutoMapper Mapperly
EF Core ProjectTo ✅ Mapsicle.EntityFramework ✅ Built-in ✅ (expressions)
FluentValidation ✅ Mapsicle.Validation ❌ ❌
DataAnnotations ✅ Mapsicle.DataAnnotations ❌ ❌
JSON serialization ✅ Mapsicle.Json ❌ ❌
ASP.NET Core ✅ Mapsicle.AspNetCore ❌ ❌
Caching ✅ Mapsicle.Caching ❌ N/A
Audit/Change tracking ✅ Mapsicle.Audit ❌ ❌
Naming conventions ✅ 5 conventions ✅ Built-in ✅ NamingStrategy

Naming Convention Support

Convention Mapsicle AutoMapper Mapperly
PascalCase ✅ ✅ ✅
camelCase ✅ ✅ ✅
snake_case ✅ ✅ ✅
kebab-case ✅ ❌ ❌
SCREAMING_SNAKE_CASE ✅ ❌ ❌

Performance Characteristics

Measured rather than described. One order aggregate, nine types, three levels of nesting, two collections, mapped on an Apple M1 under .NET 8 Release, against the same projection written out by hand. Reproduce it from github.com/arnelirobles/mapsicle_samples.

All seven lanes in one process, each checked against the hand written baseline member by member before a single timing is taken, because a mapper that drops a member is faster than one that does not.

Lane Mean vs hand written Allocated
hand written 288.5 ns 1.00 1.41 KB
Mapsicle, pair declared 287.7 ns 1.00 1.41 KB
Mapperly 4.1.1 312.8 ns 1.08 1.50 KB
Mapster 7.4.0 313.7 ns 1.09 1.38 KB
Mapsicle, declared, untyped call 316.1 ns 1.10 1.41 KB
Mapsicle, nothing declared 519.5 ns 1.80 1.41 KB
AutoMapper 15.1.3 715.8 ns 2.48 1.48 KB

Read the middle of that table as a range, not a ranking. Mapperly and Mapster are 1.08 and 1.09 here and have swapped places between runs. The three modern mappers sit inside ten percent of each other and of hand written code, and nobody migrates a codebase for ten percent.

What holds across every run is the two ends. A declared pair is level with hand written code and allocates the same. An undeclared pair is 1.80, which is still 1.4x faster than AutoMapper and needs no setup at all.

Mapster allocates the least of anyone, including hand written code. Mapperly is the only lane above the baseline, and that is one habit rather than anything structural: its collection helpers take IReadOnlyCollection<T> where the member is a List<T>, so every foreach boxes the struct enumerator. The section on compile-time mapping shows both emitted loops side by side.

Mapsicle AutoMapper Mapperly Mapster
First map of a pair 2,480 ns declared, 367,138 ns not high, it compiles too none, it is already code it compiles at startup
Startup time impact none for declared pairs medium none compiles on first use or at startup
AOT compatible declared pairs yes, undeclared no no yes with its codegen tool

When to Use Each

Scenario Recommendation Why
Fastest warm mapping Mapsicle, pair declared 1.00x hand written, against 1.08 and 1.09 for Mapperly and Mapster. Ten percent, which is a tiebreaker rather than a reason
The compiler must prove every pair maps Mapperly A pair it cannot generate does not compile. Mapsicle warns and falls back, which is safer at run time and weaker as a guarantee
AOT, and nothing may fall back to reflection Mapperly Mapsicle's declared pairs are AOT clean, but an undeclared one throws NotSupportedException at run time. Mapperly has no such path to leave open by accident
AOT, and you will declare every pair Mapsicle or Mapperly Both work. Check the build for MSG001 if you pick Mapsicle
An object graph with reference cycles AutoMapper, or Mapsicle AutoMapper preserves the reference so the cycle survives intact. Mapsicle terminates and returns something usable, but expands the cycle to MaxDepth (32) nested copies before it stops. Mapperly and Mapster both abort the process on default settings, and both are correct with one line of configuration
Quick prototyping, zero setup Mapsicle or Mapster Neither asks for configuration. Mapsicle additionally lets you add the generator later without touching a call site
A large graph you do not want to declare Mapsicle or Mapster Neither needs a line of setup. An undeclared Mapsicle pair is 1.80x hand written and still 1.4x faster than AutoMapper; Mapster is 1.09x with no declaration at all
Need integrated validation Mapsicle Mapsicle.Validation, no equivalent in any of the other three
Existing AutoMapper codebase AutoMapper (if licensed) or migrate
Budget-conscious or OSS project Mapsicle, Mapperly or Mapster All three MIT. Permissive, and each asks only that the copyright and permission notice travels with the code
Complex mapping configurations AutoMapper or Mapsicle (fluent)
ASP.NET Core Minimal APIs Mapsicle (AspNetCore package)
Need audit trail of changes Mapsicle (Audit package)

Two of those rows go to Mapperly on purpose. Its guarantee is stronger than Mapsicle's precisely because it has no fallback: if it cannot emit a mapper you find out at compile time, every time. Mapsicle trades that for a mapper that always works, and the cost of the trade is that a MSG001 you did not read is a pair running 1.80x instead of 1.00x.

Mapster shares several rows, and that is the honest picture rather than an oversight. It is MIT, it maps by convention with no setup, and its configuration API is deliberately shaped like AutoMapper's so a port is mechanical. Where Mapsicle differs is the pair of things no single competitor offers together: no configuration to start, and hand written speed when you want it, without the call site changing either way.

Code Comparison

Mapsicle (Static - Zero Config)

var dto = user.MapTo<UserDto>();

Mapsicle (Fluent)

var config = new MapperConfiguration(cfg => cfg.CreateMap<User, UserDto>());
var mapper = config.CreateMapper();
var dto = mapper.Map<UserDto>(user);

AutoMapper

var config = new MapperConfiguration(cfg => cfg.CreateMap<User, UserDto>());
var mapper = config.CreateMapper();
var dto = mapper.Map<UserDto>(user);

Mapperly

[Mapper]
public partial class UserMapper
{
    public partial UserDto ToDto(User user);
}
// Usage
var dto = new UserMapper().ToDto(user);

Unique Mapsicle Features

Features not found in AutoMapper or Mapperly:

  1. Static zero-config API: user.MapTo<UserDto>() - no setup required
  2. Built-in validation integration: Map + validate in one call with FluentValidation or DataAnnotations
  3. Audit/diff tracking: Track what changed during mapping with MapWithAudit<T>()
  4. Caching integration: Cache mapped results with IMemoryCache/IDistributedCache
  5. ASP.NET Core IResult helpers: MapValidateAndReturn<T, TValidator>()
  6. JSON map-and-serialize: MapToJson<TDest>(), MapFromJson<TIntermediate, TDest>()
  7. LRU cache option: Memory-bounded cache for long-running applications

Clone this wiki locally