在C#企业级开发中,分层架构往往要求数据访问层的实体类与业务层、接口层的模型相互转换。如果靠手写赋值语句,不仅代码冗长,而且一旦字段增减就需同步修改多处。AutoMapper是一个轻量级的对象到对象映射工具,能够依据约定或显式配置自动完成属性映射,大幅减少样板代码。

一、AutoMapper的安装与基础配置
使用AutoMapper的第一步是通过NuGet安装对应包。目前主流版本为AutoMapper 12.x,它依赖DependencyInjection扩展包以便和.NET Core内置容器集成。在Visual Studio的包管理器控制台执行以下命令即可完成引入。
// 安装核心包与依赖注入扩展 Install-Package AutoMapper Install-Package AutoMapper.Extensions.Microsoft.DependencyInjection
安装完成后,需要在程序启动阶段向服务容器注册AutoMapper,并指定包含映射配置的程序集。框架会扫描其中继承Profile的类,自动构建映射关系。这种集中式注册方式便于维护,也避免了在业务代码中散落大量创建Mapper实例的逻辑。
// Program.cs 或 Startup.cs 中注册 using AutoMapper; using AutoMapper.Extensions.Microsoft.DependencyInjection; var builder = WebApplication.CreateBuilder(args); // 扫描当前程序集内所有Profile builder.Services.AddAutoMapper(typeof(Program).Assembly);
基础配置就绪后,便可定义具体的源类型与目标类型。AutoMapper默认按属性名称进行匹配,名称一致且类型兼容时无需额外配置。这种基于约定的映射极大降低了入门成本,让开发者专注在特殊规则的补充上。
二、使用Profile管理映射规则
Profile是AutoMapper推荐的组织映射配置的方式。每一个Profile代表一组相关的映射定义,通过重写Configure方法调用CreateMap来声明源与目标的关系。将不同模块的映射拆分到多个Profile中,可以让项目结构更清晰。
using AutoMapper;
public class UserProfile : Profile
{
public UserProfile()
{
// 基础映射:UserEntity -> UserDto
CreateMap<UserEntity, UserDto>();
}
}
public class UserEntity
{
public int Id { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
}
public class UserDto
{
public int Id { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
}
当字段名不一致时,可以使用ForMember方法显式指定目标属性的来源。例如数据库实体使用PascalCase命名,而前端模型要求camelCase,或者源对象中姓和名分开存储而目标需要全名,都可以通过自定义表达式解决。
CreateMap<UserEntity, UserDto>()
.ForMember(dest => dest.FullName,
opt => opt.MapFrom(src => src.FirstName + " " + src.LastName))
.ForMember(dest => dest.Email,
opt => opt.Condition(src => !string.IsNullOrEmpty(src.Email)));
除了正向映射,AutoMapper还支持ReverseMap以启用双向转换,但需注意反向映射可能覆盖数据库主键或审计字段,因此生产环境通常只声明单向映射并在写入时采用专门命令模型,避免意外篡改不该修改的列。
三、嵌套对象与集合映射实战
真实业务里实体常包含导航属性,比如订单附带客户信息。AutoMapper能够递归映射嵌套对象,只要对应的子类型也配置了映射规则。如果子类型未配置,框架会尝试按名称直接赋值,类型不匹配则忽略或抛异常,所以建议为所有参与转换的类型建立明确映射。
public class OrderEntity
{
public int OrderId { get; set; }
public UserEntity Owner { get; set; }
public List<OrderItemEntity> Items { get; set; }
}
public class OrderDto
{
public int OrderId { get; set; }
public UserDto Owner { get; set; }
public List<OrderItemDto> Items { get; set; }
}
// Profile中
CreateMap<OrderEntity, OrderDto>();
CreateMap<OrderItemEntity, OrderItemDto>();
集合映射不需要写循环,AutoMapper会自动遍历源集合并为每个元素执行元素映射。这一点在处理分页查询结果时尤其方便,可直接将List<Entity>转为List<Dto>。不过要留意大集合映射会产生一定开销,可结合AsNoTracking和投影查询减少不必要字段传输。
// 注入IMapper后使用
private readonly IMapper _mapper;
public OrderService(IMapper mapper) { _mapper = mapper; }
public List<OrderDto> GetOrders()
{
var entities = _dbContext.Orders.Include(o => o.Owner).ToList();
return _mapper.Map<List<OrderDto>>(entities);
}
若需在映射时忽略某些属性,可调用ForSourceMember或ForMember设置Ignore,防止敏感字段如密码哈希向外暴露。同时,使用ValidateProfileOnBuild可在开发期检查配置完整性,提前发现未映射的目标属性。
四、性能注意事项与最佳实践
AutoMapper在首次使用时会编译映射表达式并缓存,后续调用开销较小,但频繁创建Mapper实例或每次请求重建配置会带来性能浪费。应始终通过依赖注入复用单例Mapper,而非在方法内new MapperConfiguration。
// 错误示范:每次调用都重建配置 var config = new MapperConfiguration(cfg => cfg.CreateMap<A, B>()); var mapper = config.CreateMapper();
对于只读查询场景,可进一步使用ProjectTo直接生成目标类型的IQueryable,让ORM在数据库侧完成投影,避免先查实体再内存映射。这样既减少数据拉取量,也降低CPU占用,是大型列表接口推荐的用法。
public IQueryable<UserDto> GetUserQuery()
{
return _dbContext.Users
.ProjectTo<UserDto>(_mapper.ConfigurationProvider);
}
最后,映射配置应当随单元测试覆盖,验证关键字段正确转换。AutoMapper虽自动但非万能,遇到复杂业务计算仍应放在领域服务中,而非塞进映射规则里,保持映射层只负责结构转换这一单一职责。
AutoMapperCSharp对象映射修改时间:2026-08-08 04:45:29