应用场景

在上一篇文章——Asp.NetCore之AutoMapper基础篇中我们简单介绍了一些AutoMapper的基础用法以及如何在.NetCore中实现快速开发。我相信用过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。

Asp.NetCore之AutoMapper进阶篇的更多相关文章

  1. Asp.NetCore之AutoMapper基础篇

    应用场景 现在由于前后端技术的分离,后端程序员在使用ORM框架开发后台API接口的时候,往往会将数据库的"数据模型"直接提供给前端.而大多数时候,可能这些数据并不能够满足前端展示的 ...

  2. Asp.Net MVC4 系列--进阶篇之路由 (2)

    上一篇介绍了Asp.Net MVC 中,从Http Pipeline上接收到请求如何匹配,匹配限制,以及如何控制在指定命名空间查找,解析出controller和action,并传参. 这篇主要介绍如何 ...

  3. Asp.Net MVC4 系列-- 进阶篇之路由(1)

    创建一个路由 打开 RouteConfig.cs  ,发现已经创建了一个默认路由 : routes.MapRoute( name:"Default", url:"{con ...

  4. Asp.Net MVC4 系列-- 进阶篇之路由

    原文  http://blog.csdn.net/lan_liang/article/details/22993839 创建一个路由 打开 RouteConfig.cs  ,发现已经创建了一个默认路由 ...

  5. bootstrap-data-target触发模态弹出窗元素的data使用 data-toggle与data-target的作用 深入ASP.NET MVC之九:Ajax支持 Asp.Net MVC4系列--进阶篇之AJAX

    bootstrap-data-target触发模态弹出窗元素的data使用 时间:2017-05-27 14:22:34      阅读:4479      评论:0      收藏:0      [ ...

  6. Asp.Net MVC4 系列-- 进阶篇之路由(1)【转】

    http://blog.csdn.net/lan_liang/article/details/22993839?utm_source=tuicool

  7. 壹佰文章最全总结| 《关于ASP.NETCore的分享之路》

    学习路线图 (关于学习ASP.NET Core需要了解和掌握的知识点图) 一言不合就来图,各位博客园小伙伴大家好,感觉好久没有写文章了,自从春节开始,中间经历种种,慢慢的就开始微信公众号发文了,原因有 ...

  8. ASP.NET MVC URL重写与优化(进阶篇)-继承RouteBase玩转URL

    http://www.cnblogs.com/John-Connor/archive/2012/05/03/2478821.html 引言-- 在初级篇中,我们介绍了如何利用基于ASP.NET MVC ...

  9. [转载]ASP.NET MVC URL重写与优化(进阶篇)-继承RouteBase玩转URL

    引言-- 在初级篇中,我们介绍了如何利用基于ASP.NET MVC的Web程序中的Global文件来简单的重写路由.也介绍了它本身的局限性-依赖于路由信息中的键值对: 如果键值对中没有的值,我们无法将 ...

随机推荐

  1. kubernetes-1.18.2集群安装-02

    一.基础配置 修改主机名 # 在 172.17.32.23 上:hostnamectl set-hostname k8s-master01bash​# 在 172.17.32.38 上:hostnam ...

  2. 重置GrindConrol焦点行FocusedRowHandle

    List<model> list=this.CurrentList; var selectModel=tempselectmodel; //找selectModel在list中得位置 va ...

  3. Pandas_VBA_数据分类比较

    Python与VBA的比较2 需求: input文件中有两列数据,第一列为Name,第二列为Score,Name列里有重复的值,要求按照name的唯一值统计 score,输出到output文件按中. ...

  4. kettle——作业

    使用作业执行之前的转换,并且额外在表student2中添加一条数据 这里操作类似hue (1)新建一个作业,拉取组件 选择start 组件名字,类型可以下拉如图,根据需要选择即可 选择转换 并将sta ...

  5. Canvas实现放大镜效果完整案例

    本文主要记录 canvas 在图像.文字处理.离屏技术和放大镜特效的实现过程中使用到的API.先看下效果吧: 一张模糊的图片: 鼠标点击任意位置,产生放大效果: 哇塞~ 一个帅哥,哈哈哈哈~ 1.显示 ...

  6. SQL Server 批量生成数据库内多个表的表结构

    在遇到大型数据库的时候,一个数据库内存在大量的数据表,如果要生成多个表的表结构,一个表的检索,然后右键Create出来,太麻烦了. 下面我介绍下批量选择并生成多个表的表结构快捷方式,仅供参考. 第一步 ...

  7. Unity CommandBuffer物体轮廓

    1.command buffer具有很高的灵活性,它的作用是预定义一些渲染指令,然后在我们想要执行的时候去执行这些指令(见图1),绿点表示可以在"Forward Rendering Path ...

  8. Nginx 解析漏洞复现

    一.漏洞描述 该漏洞与nginx.php版本无关,属于用户配置不当造成的解析漏洞 二.漏洞原理 1.由于nginx.conf的如下配置导致nginx把以'.php'结尾的文件交给fastcgi处理,为 ...

  9. JWT(JSON Web Token)入门

    简介 JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案 一.跨域认证的问题 互联网服务离不开用户认证.一般流程是下面这样. 1.用户向服务器发送用户名和密码. 2.服务器验证 ...

  10. 这次齐了!Java面向对象、类的定义、对象的使用,全部帮你搞定

    概述 Java语言是一种面向对象的程序设计语言,而面向对象思想是一种程序设计思想,我们在面向对象思想的指引下, 使用Java语言去设计.开发计算机程序. 这里的对象泛指现实中一切事物,每种事物都具备自 ...