Mapster 在正确配置下比 AutoMapper 更快,但需避开反射、提前注册或启用 Source Generator;首次调用 Adapt() 会触发懒编译导致延迟,未初始化易引发 NullReferenceException。

Mapster 在大多数真实场景下确实比 AutoMapper 快,但“快”不是无条件的——它依赖你用对模式、避开反射路径、提前注册配置。直接调用 Adapt() 而不做初始化,首次映射可能比 AutoMapper 还慢;而开启编译时生成(Source Generator)后,映射开销可逼近手写赋值。
安装后不初始化就调用 Adapt() 会怎样?
Mapster 的 Adapt() 扩展方法在首次使用某组类型映射时,会动态构建表达式树并编译为委托。这个过程是线程安全但有开销的,尤其在高并发 API 首次请求时可能触发明显延迟(比如 10–50ms),且伴随 CPU 尖峰。
- 这不是 bug,而是“懒编译”设计:省去启动时全量预热成本,换来了首次映射的冷启动代价
- 若你没调用
TypeAdapterConfig.GlobalSettings.Compile()或没启用 Source Generator,后续每次同类型映射虽复用委托,但配置解析仍走反射路径,性能优势打折扣 - 典型错误现象:
NullReferenceException报在Adapt()行,往往是因为没引用using Mapster;导致扩展方法不可见,而非映射逻辑问题
TypeAdapterConfig 配置时机和作用域很关键
全局配置(TypeAdapterConfig.GlobalSettings)应在应用启动早期执行,比如 Program.cs 的最顶部;局部配置(TypeAdapterConfig<tsource tdestination>.NewConfig()</tsource>)只对显式指定的类型对生效,且必须在首次映射前完成注册。
- 重复调用
NewConfig()同一组类型,会覆盖前一次配置——不是合并,是替换 - 嵌套对象映射(如
UserEntity.Address.Street → UserDto.StreetAddress)默认启用扁平化,但若目标属性叫StreetAddr而非StreetAddress,需手动Map(dest => dest.StreetAddr, src => src.Address.Street) - 条件映射别写在
AfterMapping里做业务判断:它总被执行。真要过滤,用Map的重载带condition参数,例如.Map(dest => dest.Status, src => src.IsActive ? "Active" : "Inactive", src => src.IsActive)
想真正榨干性能?必须用 Source Generator
Mapster 7.4+ 支持 Source Generator 模式,它在编译期生成纯 C# 映射代码(类似手写),彻底绕过运行时表达式编译和反射。生成的代码会被 IDE 索引,还能调试——这是 AutoMapper 做不到的。
- 只需安装
Mapster.SourceGenerator包,并在.csproj中添加<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles> - 生成的文件默认输出到
obj/Debug/net8.0/generated/Mapster*,你可以打开看——全是干净的new TDestination { ... } = ...赋值语句 - 注意:Generator 不处理运行时才确定的类型(比如
object或dynamic),这类仍回落到动态委托路径 - 若你用 EF Core 的
AsNoTracking().ProjectToType<TDto>(),Generator 同样生效,LINQ 表达式树直接内联映射逻辑,避免中间实体实例化
最容易被忽略的一点:Mapster 的“快”,建立在类型结构稳定、命名可预测的前提下。一旦大量使用 ConvertUsing 自定义转换器或频繁映射 Dictionary<string, object> 这类弱类型数据,性能优势会快速收窄——这时候该反思的不是库选得对不对,而是数据契约本身是否该重构。



















