Mapster 的 Adapt<T>() 会应用 IRegister 自定义映射吗?
不需要使用 IMapper 也能应用 IRegister 中的自定义映射。source.Adapt<T>() 默认读取 TypeAdapterConfig.GlobalSettings,会使用已应用到全局配置的规则。Mapster 的 TypeAdapter 源码
如果规则只注册在独立的 TypeAdapterConfig 实例中,就需要调用 source.Adapt<T>(config),或者使用基于该实例创建的 mapper。仅凭两种写法返回相同结果,无法判断自定义映射是否生效。
IRegister 的规则何时生效?
IRegister 用来组织映射规则。定义实现类后,还需要将规则应用到某个配置实例,例如调用该实例的 Scan(...) 或 Apply(...)。普通的 Adapt<T>() 调用不会自行扫描程序集,也不会从依赖注入容器中寻找 IRegister。Mapster 配置注册文档
不同调用方式使用的配置如下:
| 调用方式 | 使用的配置 |
|---|---|
source.Adapt<ResultDTO>() | TypeAdapterConfig.GlobalSettings |
source.Adapt<ResultDTO>(config) | 显式传入的 config |
new MapsterMapper.Mapper(config).Map<ResultDTO>(source) | 构造时传入的 config |
_mapper.Map<ResultDTO>(source) | 被注入 mapper 实例实际持有的配置 |
Mapster 自带的 Mapper 无参构造函数使用全局配置;传入配置的构造函数会保存该实例,并在映射时通过它获取映射函数。因此,使用 IMapper 并不意味着它一定读取全局配置。Mapster 的 Mapper 源码
使用全局配置时如何注册?
下面使用独立的示例类型演示自定义字段映射,不假设原问题中的 Result 和 ResultDTO 具有哪些属性:
using Mapster;
public class Person
{
public string Name { get; set; } = "";
}
public class PersonDto
{
public string DisplayName { get; set; } = "";
}
public class PersonRegister : IRegister
{
public void Register(TypeAdapterConfig config)
{
config.NewConfig<Person, PersonDto>()
.Map(dest => dest.DisplayName, src => src.Name);
}
}
在应用启动阶段,将规则扫描到全局配置:
TypeAdapterConfig.GlobalSettings.Scan(
typeof(PersonRegister).Assembly);
之后直接调用 Adapt<T>() 就会使用该规则:
var person = new Person { Name = "Ada" };
var dto = person.Adapt<PersonDto>();
// 按上述规则,dto.DisplayName 为 "Ada"。
这里通过 typeof(PersonRegister).Assembly 指定包含注册类的程序集。如果规则分布在多个程序集中,需要扫描相应程序集。也可以显式应用某个注册类:
TypeAdapterConfig.GlobalSettings.Apply(new PersonRegister());
Scan 和 Apply 是两种注册方式,通常选一种即可。Mapster 配置注册文档
使用独立配置时如何调用?
如果应用为不同场景维护独立配置,应复用对应实例,并在调用时明确使用它:
using Mapster;
using MapsterMapper;
var config = new TypeAdapterConfig();
config.Scan(typeof(PersonRegister).Assembly);
var person = new Person { Name = "Ada" };
var dto1 = person.Adapt<PersonDto>(config);
var mapper = new Mapper(config);
var dto2 = mapper.Map<PersonDto>(person);
这两次映射使用相同的 config。如果改为 person.Adapt<PersonDto>(),读取的仍是全局配置;局部变量名为 config,或者它已注册到 DI 容器,都不会改变这一点。Mapster 文档支持将配置实例传给 Adapt 或 Mapper。Mapster 独立配置文档
对于原问题中的仓储代码,应检查应用启动时如何创建和注册 IMapper。如果规则扫描到了独立配置,且注入的 mapper 使用该配置,_mapper.Map<ResultDTO>(res) 就能读取这些规则;无配置参数的 res.Adapt<ResultDTO>() 仍然读取全局配置。
为什么两个结果一样?
可能是 mapper 使用了全局配置,也可能是当前数据只涉及默认约定即可完成的映射,或者自定义规则对本次输入没有造成可见差异。原问题没有提供 IRegister 内容和 DI 注册代码,因此无法确定具体原因。
可以选择一个必须依赖自定义规则才能得到的字段,例如上例中的 Name → DisplayName,分别检查全局配置调用、显式配置调用和 mapper 调用的结果。只比较同名字段,通常不足以证明自定义规则已经执行。
还有哪些注意事项?
- 在首次执行映射前完成配置注册,并复用配置实例,避免在每次请求中重新扫描或创建配置。Mapster 配置生命周期说明
- 如果映射表达式通过
MapContext.Current.GetService<T>()解析 DI 服务,应按依赖注入文档使用ServiceMapper执行映射。直接传入配置的Adapt<T>(config)本身不会建立相同的服务解析上下文。Mapster 依赖注入文档 - 从 AutoMapper 迁移时,确认注入的接口是
MapsterMapper.IMapper,避免与AutoMapper.IMapper混淆。 - 原代码的
FirstOrDefaultAsync()可能查不到记录,需要明确空结果的处理方式;当前查询也没有使用传入的id。这两点应单独处理,不影响配置来源的判断。
备注:内容仅供参考。
