在 ASP.NET Core 开发中,我们经常需要在不同层之间传递数据:比如从数据库模型(Entity)转换到 DTO,再从 DTO 转换为前端视图模型。这些转换代码大量重复、冗长、容易出错。为了解决这个问题,AutoMapper 诞生了。
本文主要介绍在WebApi(.net 8)环境中使用AutoMapper 14.0.0
官方文档地址:AutoMapper — AutoMapper documentation
一、什么是 AutoMapper?
AutoMapper 是一个 .NET 类库,它能自动在两个类型之间做映射,尤其是当这些类型拥有相似属性时。它减少了手动编写 obj.Prop = dto.Prop
的重复性劳动。
public class Student
{public string Name { get; set; }public int Age { get; set; }public string Address{ get; set; }public string Nickname{ get; set; }
}public class Dto_Student
{public string Name { get; set; }public int Age { get; set; }
}
可以直接通过 AutoMapper 在这两者之间做转换,而无需显式赋值。
二、基本用法:从零开始配置
1.安装 NuGet 包
dotnet add package AutoMapper --version 14.0.0
13.0开始无需引入AutoMapper.Extensions.Microsoft.DependencyInjection
2.配置映射关系
创建一个映射配置 Profile 类:
public class StudentProfile : Profile
{public StudentProfile(){CreateMap<Student, Dto_Student>();CreateMap<Dto_Student, Student>();}
}
3.在 Program.cs中注册
using AutoMapper;namespace AutoMapperProfileConfig
{/// <summary>/// 标记程序集哑类/// </summary>public class AutoMapperProfileConfigMarker : Profile{public AutoMapperProfileConfigMarker(){}}
}
builder.Services.AddAutoMapper(typeof(AutoMapperProfileConfigMarker).Assembly);
建议专门建立一个类库,同时为每个表的映射创建一个映射类,然后创建一个专门标记程序集而不做其他事的一个哑类.
这样可以自动扫描该类库里面的所有的Profile类(还有几种其他方法,可自行查看)
4.依赖注入使用映射服务
[ApiController]
[Route("api/[controller]")]
public class StudentController : ControllerBase
{private readonly YourDbContext _context;private readonly IMapper _mapper;public StudentController(YourDbContext context, IMapper mapper){_context = context;_mapper = mapper;}// GET: api/Student[HttpGet]public async Task<ActionResult<IEnumerable<Dto_Student>>> GetStudents(){// 从数据库获取 Student 实体列表var students = await _context.Students.ToListAsync();// 使用 AutoMapper 将实体列表映射为 DTO 列表var studentDtos = _mapper.Map<List<Dto_Student>>(students);// 返回给前端return Ok(studentDtos);}
}
5.命名约定
默认情况映射的属性名必须完全一致,但你可以配置实现特殊的映射关系.
AutoMapper 中命名约定的优先级关系如下:
优先级从高到低:
-
CreateMap().ForMember()
明确指定字段映射 -
在
Profile
中设置的命名约定 -
全局设置(MapperConfiguration 中配置)
-
默认行为(ExactMatchNamingConvention)
public class Source
{public string user_id { get; set; }
}public class Destination
{public string UserId { get; set; }
}
情况 1:使用 .ForMember()
明确映射
CreateMap<Source, Destination>().ForMember(dest => dest.UserId, opt => opt.MapFrom(src => src.user_id));
优先级最高,无论有没有配置命名约定,它都生效
情况 2:Profile 中设置命名约定
public class MyProfile : Profile
{public MyProfile(){SourceMemberNamingConvention = LowerUnderscoreNamingConvention.Instance;DestinationMemberNamingConvention = PascalCaseNamingConvention.Instance;CreateMap<Source, Destination>();}
}
会生效,但仅限于此 Profile
中的 CreateMap
映射。
情况 3:全局配置命名约定
services.AddAutoMapper(cfg =>
{cfg.SourceMemberNamingConvention = LowerUnderscoreNamingConvention.Instance;cfg.DestinationMemberNamingConvention = PascalCaseNamingConvention.Instance;
}, typeof(SomeMarker));
会自动应用到所有未手动指定命名规则的 Profile 中的映射
情况 4:都不配置时,默认使用 ExactMatch
即只匹配名字完全一致(区分大小写)的属性名
设置方式 | 作用范围 | 优先级 | 备注 |
---|---|---|---|
.ForMember() | 某个字段映射 | 🥇 最高 | 覆盖一切 |
Profile 中命名约定 | 当前 Profile | 🥈 中高 | 优于全局配置 |
cfg.SourceMemberNamingConvention | 所有 Profile | 🥉 中等 | 默认使用 |
不设置 | 使用 ExactMatch | 最低 | 仅匹配完全相同 |