对象映射的概念
将一个对象的数据根据特定规则批量映射到另一个对象中,减少手工操作和降低人为出错率。如将 DTO 对象映射到 Entity 实体中,反之亦然。
silky 框架支持以下对象映射工具:
| 映射工具 | NuGet 包 | 说明 |
|---|---|---|
| AutoMapper | Silky.ObjectMapper.AutoMapper | 业界主流映射工具,功能强大(已包含在 Silky.Agent.Host 中) |
| Mapster | Silky.ObjectMapper.Mapster | 高性能映射工具,支持代码生成 |
使用 AutoMapper 作为映射工具
步骤一:依赖 AutoMapperModule 模块
在自定义启动模块中显式依赖 AutoMapperModule。
注意:如果使用框架默认的
DefaultWebHostModule、DefaultGeneralHostModule等模块,AutoMapperModule已内置依赖,无需额外配置。
如果是自定义启动模块:
[DependsOn(
typeof(AutoMapperModule), // 显式依赖 AutoMapper 模块
typeof(DotNettyTcpModule),
// ... 其他模块
)]
public class CustomHostModule : GeneralHostModule
{
}
步骤二:定义映射配置(Profile)
通过继承 Profile 基类,在构造器中定义源类型与目标类型的映射关系:
public class AccountProfile : Profile
{
public AccountProfile()
{
// DTO -> Entity
CreateMap<CreateAccountInput, Account>();
// Entity -> DTO
CreateMap<Account, GetAccountOutput>();
// 带自定义映射逻辑的 DTO -> Entity
CreateMap<UpdateAccountInput, Account>()
.AfterMap((src, dest) =>
{
dest.UpdateTime = DateTime.Now;
dest.UpdateBy = NullSession.Instance.UserId;
});
}
}
步骤三:使用 MapTo 扩展方法进行映射
silky 提供了 MapTo<T>() 扩展方法,可以对任意对象调用:
public async Task<GetAccountOutput> CreateAsync(CreateAccountInput input)
{
// 输入 DTO 映射为实体
var account = input.MapTo<Account>();
account = await _accountDomainService.CreateAsync(account);
// 实体映射为输出 DTO
return account.MapTo<GetAccountOutput>();
}
public async Task UpdateAsync(UpdateAccountInput input)
{
var account = await GetAccountByIdAsync(input.Id);
// 用输入对象更新已有实体的属性(增量更新)
account = input.MapTo(account);
await _accountRepository.UpdateAsync(account);
}
集合映射
silky 的 MapTo<T>() 扩展方法同样支持集合类型映射,AutoMapper 会自动处理集合内每个元素的映射:
// List<T> 映射
public async Task<IList<GetAccountOutput>> GetListAsync()
{
var accounts = await _accountRepository.GetListAsync();
// List<Account> → List<GetAccountOutput>
return accounts.MapTo<List<GetAccountOutput>>();
}
// IEnumerable<T> 映射
public IEnumerable<GetAccountOutput> GetAll(IEnumerable<Account> accounts)
{
return accounts.MapTo<IEnumerable<GetAccountOutput>>();
}
集合映射无需额外配置,只要元素类型的映射关系(CreateMap<Account, GetAccountOutput>())已在 Profile 中定义,集合类型会自动支持。
使用 Mapster 作为映射工具
如果需要更高性能的映射,可以选择 Mapster。安装 Silky.ObjectMapper.Mapster 包:
PM> Install-Package Silky.ObjectMapper.Mapster
在自定义模块中依赖 MapsterModule:
[DependsOn(typeof(MapsterModule))]
public class CustomHostModule : GeneralHostModule { }
Mapster 的使用方式与 AutoMapper 的 MapTo<T>() 扩展方法完全一致,无需修改业务代码,只需替换依赖的模块即可:
// Mapster 同样通过 MapTo<T>() 调用,业务代码写法不变
var output = entity.MapTo<GetAccountOutput>();
var list = entities.MapTo<List<GetAccountOutput>>();
Mapster 自定义映射配置
Mapster 通过静态配置实现自定义映射规则,在应用启动时注册:
public class AccountMappingConfig : IRegister
{
public void Register(TypeAdapterConfig config)
{
config.NewConfig<CreateAccountInput, Account>()
.Map(dest => dest.CreateTime, src => DateTime.Now)
.Map(dest => dest.Status, src => AccountStatus.Active);
config.NewConfig<Account, GetAccountOutput>()
.Map(dest => dest.FullName, src => $"{src.FirstName} {src.LastName}");
}
}
框架会自动扫描实现了 IRegister 接口的类并注册配置。
选型建议
| 场景 | 推荐工具 |
|---|---|
| 一般业务场景 | AutoMapper,已内置于 Silky.Agent.Host,无需额外安装 |
| 高性能场景 | Mapster,性能优于 AutoMapper,尤其在高频映射的热路径中 |
| 需要代码生成(零反射) | Mapster,支持 Source Generator 模式生成静态映射代码 |
两者的 MapTo<T>() API 完全兼容,切换时无需修改业务代码,只需更换所依赖的模块。
// 列表映射
var accounts = await _accountRepository.GetListAsync();
var outputs = accounts.MapTo<List<GetAccountOutput>>();
使用 Mapster 作为映射工具
如果需要更高性能的映射,可以选择 Mapster。安装 Silky.ObjectMapper.Mapster 包:
PM> Install-Package Silky.ObjectMapper.Mapster -Version 3.9.2
在自定义模块中依赖 MapsterModule:
[DependsOn(typeof(MapsterModule))]
public class CustomHostModule : GeneralHostModule
{
}
Mapster 的使用方式与 AutoMapper 的 MapTo<T>() 扩展方法一致,只需替换依赖的模块即可。
选择建议
- 中小型项目:推荐使用 AutoMapper,社区资源丰富,文档完善
- 高性能场景:推荐使用 Mapster,性能优于 AutoMapper,且支持代码生成(零反射)
