应用场景

我相信用过AutoMapper实现模型映射之后,许多人会和我当初一样有一种淡淡的忧愁,每次实现自定义映射都需要手写映射规则,形如:

CreateMap<Order, OrderDTO>().ForMember(dest => dest.OrderName, src => src.MapFrom(s => s.Name))

如果有很多的模型需要映射,并且映射规则基本都一致,譬如:模型字段不一致映射(Order.Name映射到OrderDTO.OrderName),如果存在很多类似这样的模型属性映射, 大量的手动编码同样效率很低,不禁抛出疑问:是否可以批量动态映射呢?

AutoMapper实现动态映射

既然有了以上的场景需求,下面我们就来聊一聊如何使用AutoMapper实现动态映射。AutoMapper框架为我们提供了动态映射方法,如下

IMappingExpression CreateMap(Type sourceType, Type destinationType, MemberList memberList)

从方法入参Type类型我们可以知道,调用该方法时我们不需要知道映射的源模型和目标模型具体是什么类型,这也就为我们实现批量映射提供了入口,对于一批有着同样映射规则的模型,我们完全可以通过该来实现。那么,我们如何批量获取需要映射的源模型和目标模型呢?下面我们结合System.Attribute特性来给大家介绍下。

Attribute特性

可能有些人没用过Attribute特性,我们先来简单了解下。Attribute特性在.Net 反射中经常被使用,它以一种声名式标签的形式存在,标签中定义了一些元素用来在程序运行时使用,它通常放置在类、属性等元素上面并用中括号[ ]的形式表达。

特性介绍:

  • 自定义特性

通常我们需要自定义特性以满足实际需求,自定义特性时必须要继承Attribute抽象类。

public class TypeMapperAttribute : Attribute
{
}
  • 预定义特性AttributeUsage

预定义特性AttributeUsage用来定义特性的一些使用规则。

[AttributeUsage(AttributeTargets.Class, Inherited = true)]
public class TypeMapperAttribute : Attribute
{}

常用参数:

  • ValidOn 规定特性可被使用的范围。它是枚举器 AttributeTargets的值的组合。默认值是 AttributeTargets.All表示在所有元素都可使用。如果只允许在类或者属性上使用可以定义:AttributeTargets.Class或者AttributeTargets.Property
  • AllowMultiple(可选的)bool类型。如果为 true,则该特性是多用的。默认值是 false(单用的)。
  • Inherited(可选的)bool类型。如果为 true,则该特性可被派生类继承。默认值是 false(不被继承)。

自定义特性:

//AttributeUsage用与指定声明的特性的使用范围  [AttributeUsage(AttributeTargets.Class| AttributeTargets.Class, Inherited = true)]public class TypeMapperAttribute : Attribute{/// <summary>/// 源类型/// </summary>public Type SourceType { get; set; }}//AttributeUsage用与指定声明的特性的使用范围  [AttributeUsage(AttributeTargets.Property, Inherited = true)]public class PropertyMapperAttribute : Attribute{/// <summary>/// 属性名称/// </summary>public string SourceName { get; set; }/// <summary>/// 数据类型/// </summary>public Type SourceDataType { get; set; }}

有了特性功能的加入,我们便可以批量获取所有需要映射的目标模型。

//获取所有需要依据特性进行映射的DTO类
var typeList = Assembly.GetAssembly(typeof(OrderDTO)).GetTypes().Where(t => t.GetCustomAttributes(typeof(TypeMapperAttribute)).Any()).ToList();
  • Assembly.GetAssembly(typeof(OrderDTO)).GetTypes() 获取指定程序集下面的所有类
  • GetCustomAttributes() 获取自定义特性
    

回到AutoMapper框架的动态映射方法CreateMap(Type sourceType, Type destinationType, MemberList memberList),我们已经有了批量的目标模型,还缺少批量的源模型。很显然,只要在目标模型上加上“特性”我们就能很容易拿到目标模型所对应的源模型。

新建基于特性的目标模型:

/// <summary>/// 源模型Order 映射到 目标模型OrderBatchDTO/// </summary>[TypeMapper(SourceType = typeof(Order))]public class OrderBatchDTO{public int Id { get; set; }/// <summary>/// Order.Name 映射到 OrderBatchDTO.OrderName/// </summary>[PropertyMapper(SourceName = "Name")]public string OrderName { get; set; }public decimal Price { get; set; }/// <summary>/// Order.CreateTime时间格式  映射到  OrderBatchDTO.CreateTime自定义字符串格式/// </summary>[PropertyMapper(SourceDataType = typeof(DateTime))]public string CreateTime { get; set; }public int CustomId { get; set; }}

通过TypeMapperAttribute特性,我们可以拿到目标模型所对应的源模型;

通过PropertyMapperAttribute特性,我们可以拿到映射规则中定义的源模型字段名称、源模型字段类型;

自定义动态映射配置文件

接下来,自定义动态映射配置文件,继承AutoMapper的Profile配置类。

public class BatchMapperProfile : Profile{public BatchMapperProfile(){InitMapper();}public void InitMapper(){//获取所有需要依据特性进行映射的DTO类var typeList = Assembly.GetAssembly(typeof(OrderDTO)).GetTypes().Where(t => t.GetCustomAttributes(typeof(TypeMapperAttribute)).Any()).ToList();typeList.ForEach(type =>{//获取类指定的特性var attribute = (TypeMapperAttribute)type.GetCustomAttributes(typeof(TypeMapperAttribute)).FirstOrDefault();if (attribute == null || attribute.SourceType == null)return;//类映射var mapper = CreateMap(attribute.SourceType, type);//处理类中映射规则不同的属性var propertyAttributes = type.GetProperties().Where(p => p.GetCustomAttributes(typeof(PropertyMapperAttribute)).Any()).ToList();propertyAttributes.ForEach(property =>{//获取属性指定特性var propertyAttribute = (PropertyMapperAttribute)property.GetCustomAttributes(typeof(PropertyMapperAttribute)).FirstOrDefault();if (propertyAttribute == null)return;if (!string.IsNullOrEmpty(propertyAttribute.SourceName)){//属性名称自定义映射mapper.ForMember(property.Name, src => src.MapFrom(propertyAttribute.SourceName));}if (propertyAttribute.SourceDataType != null && propertyAttribute.SourceDataType == typeof(DateTime)){//DateTime数据类型 映射 自定义字符串格式mapper.ForMember(property.Name, src => src.ConvertUsing(new FormatBatchConvert()));}});});}}/// <summary>/// DateTime映射到String/// </summary>public class FormatBatchConvert : IValueConverter<DateTime, string>{public string Convert(DateTime sourceMember, ResolutionContext context){if (sourceMember == null)return DateTime.Now.ToString("yyyyMMddHHmmssfff");return sourceMember.ToString("yyyyMMddHHmmssfff");}}

动态映射配置文件中主要是用了一些反射的基础知识,包括获取类型,获取指定类型属性,获取类型特性,获取属性特性等,这里就不一一介绍了。

其中,如下两个成员自定义映射规则,实际上就是我们上一篇博文中介绍的两种常用方式,差别只是动态映射方法提供的调用方式不同而已。

  • mapper.ForMember(property.Name, src => src.MapFrom(propertyAttribute.SourceName));
  • mapper.ForMember(property.Name, src => src.ConvertUsing(new FormatBatchConvert()));

有了我们自定义的动态映射配置文件之后,我们只需要在服务中依赖注入一下即可使用。.NetCore项目中如何依赖注入AutoMapper可参见上一篇博文,我这里就不再具体描述,下面我们直接使用看效果。

/// <summary>/// 批量动态映射/// </summary>/// <returns></returns>public async Task<List<OrderBatchDTO>> QueryBatch(){var orderList = await dBContext.DB.Queryable<Order>().ToListAsync();var orderDtoList = mapper.Map<List<OrderBatchDTO>>(orderList);return await Task.FromResult(orderDtoList);}

其中,mapper是我们依赖注入的AutoMapper实例。

实现效果

1)“源模型”Order类型中的Name属性值  映射到  “目标模型”OrderBatchDTO类型中的OrderName

2)“源模型”Order类型中的CreateTime属性DateTime数据类型  映射到  “目标模型”OrderBatchDTO类型中的CreateTime属性string数据类型

小结

本篇文章中,我们介绍了基于AutoMapper如何实现批量动态映射,比较适用于有很多模型需要映射且每个模型映射规则比较相同的应用场景。如果映射的模型数量较少或者映射规则五花八门,我们大可不必大费周折,手动编码也有它存在的意义。文章案例中我只用到了一对模型映射,大家可能感受不深,感兴趣的小伙伴可以看下博文源码,里面包含了多个动态映射类,小弟不才,在此献上源码地址:https://github.com/chenxf1117/Asp.NetCore-AutoMapper。

.NetCore之AutoMapper进阶篇相关推荐

  1. Enterprise Library Step By Step系列(十二):异常处理应用程序块——进阶篇

    一.把异常信息Logging到数据库 在日志和监测应用程序块中,有朋友提意见说希望能够把异常信息Logging到数据库中,在这里介绍一下具体的实现方法. 1.创建相关的数据库环境: 我们可以用日志和监 ...

  2. Docker 数据卷之进阶篇

    Docker 数据卷之进阶篇 原文:Docker 数据卷之进阶篇 笔者在<Docker 基础 : 数据管理>一文中介绍了 docker 数据卷(volume) 的基本用法.随着使用的深入, ...

  3. C#使用Xamarin开发可移植移动应用进阶篇(7.使用布局渲染器,修改默认布局),附源码...

    原文:C#使用Xamarin开发可移植移动应用进阶篇(7.使用布局渲染器,修改默认布局),附源码 前言 系列目录 C#使用Xamarin开发可移植移动应用目录 源码地址:https://github. ...

  4. Kafka核心设计与实践原理总结:进阶篇

    作者:未完成交响曲,资深Java工程师!目前在某一线互联网公司任职,架构师社区合伙人! kafka作为当前热门的分布式消息队列,具有高性能.持久化.多副本备份.横向扩展能力.我学习了<深入理解K ...

  5. 计算机编程书籍-笨办法学Python 3:基础篇+进阶篇

    编辑推荐: 适读人群 :本书适合所有已经开始使用Python的技术人员,包括初级开发人员和已经升级到Python 3.6版本以上的经验丰富的Python程序员. "笨办法学"系列, ...

  6. 最快让你上手ReactiveCocoa之进阶篇

    前言 由于时间的问题,暂且只更新这么多了,后续还会持续更新本文<最快让你上手ReactiveCocoa之进阶篇>,目前只是简短的介绍了些RAC核心的一些方法,后续还需要加上MVVM+Rea ...

  7. SQL Server调优系列进阶篇(如何维护数据库索引)

    前言 上一篇我们研究了如何利用索引在数据库里面调优,简要的介绍了索引的原理,更重要的分析了如何选择索引以及索引的利弊项,有兴趣的可以点击查看. 本篇延续上一篇的内容,继续分析索引这块,侧重索引项的日常 ...

  8. mysql 开发进阶篇系列 10 锁问题 (使用“索引或间隙锁”的锁冲突)

    1.使用"相同索引键值"的冲突 由于mysql 的行锁是针对索引加的锁,不是针对记录加的锁,所以虽然是访问不同行的记录,但如果是使用相同的索引键,是会出现锁冲突的.设计时要注意 例 ...

  9. Java进阶篇(一)——接口、继承与多态

    前几篇是Java的入门篇,主要是了解一下Java语言的相关知识,从本篇开始是Java的进阶篇,这部分内容可以帮助大家用Java开发一些小型应用程序,或者一些小游戏等等. 本篇的主题是接口.继承与多态, ...

最新文章

  1. LeetCode 583. Delete Operation for Two Strings--动态规划 DP--Java,Python,C++解法
  2. 基于ThinkPHP的在线编辑器调用
  3. 线段检测M-LSD 已开源
  4. 058_Unicode字符官方标准九
  5. Brodatz纹理图像库
  6. 让我的 .NET Core 博客系统支持 Docker
  7. 选数(洛谷P1036题题解,Java语言描述)
  8. 2-11 支付宝集福卡_实现分析
  9. 2018/11/22工作日志
  10. zerotier使用教程_ZeroTier 初阶教程
  11. 2022-2028年中国电子政务行业投资策略探讨及市场规模预测报告
  12. 大数据舆情监测平台_大数据舆情监测与分析平台有哪些?舆情大数据监测软件排名2020...
  13. Google无法验证此账号归你所有解决方法
  14. springboot和druid的整合
  15. 微信接龙,查人,查谁没有接龙,工具
  16. unity 打开项目路径无效_怎么打开已有的unity工程文件?如何打开一个已有的unity3d游戏工程啊?...
  17. c++ vector二维数组初始化与vector切片
  18. 一年303个漏洞,Chrome被评为“最脆弱”浏览器,Opera 最安全!网友:Opera 还有人用?...
  19. [架构之路-127]-《软考-系统架构设计师》-计算机网络 -1- 协议栈、网络规划与设计、网络接入技术
  20. 老年人θ-γ跨频率耦合与工作记忆表现之间的纵向关系

热门文章

  1. 2008 NBA 全明星周未之扣篮大赛
  2. 微信公众号-授权登录
  3. 人力外派和猎头的区别是什么?哪个行业更赚钱?
  4. 开源免费的WEB应用防火墙
  5. QT——可视化界面ui
  6. 【收藏】Mimics Lnnovation Suite之导出功能
  7. StringUtils.isEmpty()方法
  8. jQuery面试题答案
  9. OSI(open system internet)七层模型介绍以及NAT(Network Address Translation)技术详解
  10. greenplum安装方法详解