如今因为先后端技术的分离,后端程序员在使用ORM框架开发后台API接口的时候,每每会将数据库的“数据模型”直接提供给前端。而大多数时候,可能这些数据并不可以知足前端展现的需求,有时候可能须要在“数据模型”的基础上,加几个字段或者改几个字段展现名称或者字段展现风格,以知足前端“视图模型”的需求。遇到这种状况,后端每每须要同时定义“数据模型”和“视图模型”,并在二者之间作大量的字段赋值工做,若是“数据模型”和“视图模型”差异不大的话,这无疑很耗费心力,并且容易出错。前端
咱们先来理解两个概念“数据模型”和“视图模型”:git
“数据模型”:最简单的一种“”数据模型“”你能够把它当成是数据库表对象模型(数据模型字段一一对应数据库表结构,是数据库表的一种表现形式),使用ORM的小伙伴应该都知道,经过ORM数据库模型能够直接映射到数据表结构,能够直接操做数据库。程序员
“视图模型”:经过这个名称咱们很容易理解,视图模型是前端展现页面中所需元素的一个集合。github
当咱们将“数据模型”转换成“视图模型”提供给前端的时候,务必会产生大量的模型字段赋值的工做,模型很大的时候是否是想死的心都有了。。。AutoMapper的出现正是解决了两个模型之间枯燥无味的转换工做,用户只须要简单设置一下转换规则便可,避免了咱们每次都采用手工编写代码的方式进行转换。数据库
本文项目源码:https://github.com/chenxf1117/Asp.NetCore-AutoMapper后端
添加数据库“数据模型”:数组
/// <summary> /// 订单表 /// </summary> public class Order { /// <summary> /// ID /// </summary> public int Id { get; set; } /// <summary> /// 订单名称 /// </summary> public string Name { get; set; } /// <summary> /// 价格 /// </summary> public decimal Price { get; set; } public DateTime CreateTime { get; set; } public int CustomId { get; set; }
}
/// <summary> /// 订单子表 /// </summary> public class OrderItem { public int ItemId { get; set; } public int OrderId { get; set; } public decimal Price { get; set; } /// <summary> /// 建立时间 /// </summary> public DateTime CreateTime { get; set; } }
第二个包是.NetCore依赖注入使用。app
/// <summary> /// 订单映射模型 /// </summary> public class OrderDTO { public int Id { get; set; } /// <summary> /// 订单名称 /// </summary> public string OrderName { get; set; } public decimal Price { get; set; } public DateTime CreateTime { get; set; } public int CustomId { get; set; } }
/// <summary> /// 订单子表映射模型 /// </summary> public class OrderItemDTO { public int ItemId { get; set; } public int OrderId { get; set; } public decimal Price { get; set; } /// <summary> /// 建立时间 /// </summary> public string CreateTime { get; set; } }
在.NetCore咱们须要建立一个本身的映射配置类,该配置类必须继承AutoMapper的Profile抽象类,而后才能经过依赖注入AutoMapper的方式调用。框架
/// <summary> /// 映射配置 /// </summary> public class OrderMapperProfile : Profile { public OrderMapperProfile() { //字段名称不一致 Name映射到OrderName CreateMap<Order, OrderDTO>().ForMember(dest => dest.OrderName, src => src.MapFrom(s => s.Name)); //字段类型不一致 DateTime映射到String CreateMap<OrderItem, OrderItemDTO>().ForMember(dest => dest.CreateTime, src => src.ConvertUsing(new FormatConvert())); } } /// <summary> /// DateTime映射到String /// </summary> public class FormatConvert : IValueConverter<DateTime, string> { public string Convert(DateTime sourceMember, ResolutionContext context) { if (sourceMember == null) return DateTime.Now.ToString("yyyyMMddHHmmssfff"); return sourceMember.ToString("yyyyMMddHHmmssfff"); } }
1)自定义映射文件需继承AutoMapper抽象类“Profile”async
2)调用方法CreateMap实现模型映射
public IMappingExpression<TSource, TDestination> CreateMap<TSource, TDestination>(MemberList memberList); public IMappingExpression<TSource, TDestination> CreateMap<TSource, TDestination>();
TSource表明“数据模型”,TDestination表明“视图模型”,MemberList可选,默认0
public enum MemberList { /// <summary> /// 检查是否已映射全部目标成员 /// </summary> Destination = 0, /// <summary> /// 检查是否已映射全部源成员 /// </summary> Source = 1, /// <summary> /// 既不检查源成员也不检查目标成员,跳过验证 /// </summary> None = 2 }
例如:
CreateMap<Order, OrderDTO>();
3)调用ForMember自定义两个模型之间映射规则。
由于 AutoMapper 默认是经过匹配字段名称和类型进行自动匹配,因此若是你进行转换的两个类的中的某些字段名称不同,这里咱们就须要进行手动的编写转换规则。
IMappingExpression<TSource, TDestination> ForMember<TMember>(Expression<Func<TDestination, TMember>> destinationMember, Action<IMemberConfigurationExpression<TSource, TDestination, TMember>> memberOptions);
其中比较经常使用的:
1.两个映射模型属性/字段名称不一致。
//字段名称不一致 源类型Name映射到目标类型OrderName CreateMap<Order, OrderDTO>().ForMember(dest => dest.OrderName, src => src.MapFrom(s => s.Name));
2.两个映射模型属性/字段数据类型或展示形式不一致。
//字段类型不一致 源类型DateTime映射到目标类型String字符串 CreateMap<OrderItem, OrderItemDTO>().ForMember(dest => dest.CreateTime, src => src.ConvertUsing(new FormatConvert()));
ConvertUsing()方法中需实现成员参数接口IValueConverter中的Convert方法,实现自定义转换
void ConvertUsing<TSourceMember>(IValueConverter<TSourceMember, TMember> valueConverter);
public interface IValueConverter<in TSourceMember, out TDestinationMember> { TDestinationMember Convert(TSourceMember sourceMember, ResolutionContext context); }
自定义实现接口Convert方法,实现DateTime转string字符串:
/// <summary> /// DateTime映射到String /// </summary> public class FormatConvert : IValueConverter<DateTime, string> { public string Convert(DateTime sourceMember, ResolutionContext context) { if (sourceMember == null) return DateTime.Now.ToString("yyyyMMddHHmmssfff"); return sourceMember.ToString("yyyyMMddHHmmssfff"); } }
//注册AutoMapper //方式1 指定映射配置文件 多个可用数组表示 services.AddAutoMapper(typeof(OrderMapperProfile)); //方式2 注册程序集下面全部映射文件 services.AddAutoMapper(Assembly.Load("程序集dll名称"))
public class OrderService : IOrderService { private readonly IMapper mapper; /// <summary> /// 构造函数注入Mapper /// </summary> /// <param name="_dBContext"></param> /// <param name="_mapper"></param> public OrderService(IMapper _mapper) { mapper = _mapper; } /// <summary> /// 名称不一致 映射 /// </summary> /// <returns></returns> public async Task<List<OrderDTO>> Query() { //ORM获取数据模型数据 var orderList = await dBContext.DB.Queryable<Order>().ToListAsync(); //DTO映射 var orderDtoList = mapper.Map<List<OrderDTO>>(orderList); return await Task.FromResult(orderDtoList); } /// <summary> /// 数据类型/展示形式不一致 映射 /// </summary> /// <returns></returns> public async Task<List<OrderItemDTO>> QueryItem() { var orderList = await dBContext.DB.Queryable<OrderItem>().ToListAsync(); var orderDtoList = mapper.Map<List<OrderItemDTO>>(orderList); return await Task.FromResult(orderDtoList); } }
1)“数据模型”Order类型中的Name属性值 映射到 “视图模型”OrderDTO类型中的OrderName
2)“数据模型”OrderItem类型中的CreateTime属性DateTime数据类型 映射到 “视图模型”OrderItemDTO类型中的CreateTime属性string数据类型
本篇文章主要简单介绍下在Asp.NetCore项目中如何快速上手AutoMapper,整体来看想要上手仍是比较简单,只要掌握好几个常用的映射规则就能够了。固然,AutoMapper为咱们准备了各类自定规则的入口,有兴趣的小伙伴能够下载源码,源码中包含了不少测试用例,学习起来也比较容易理解。附上AutoMapper源码地址:https://github.com/AutoMapper/AutoMapper