应用场景

在上一篇文章——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. 使用Selenium爬取京东电商数据(以手机商品为例)

    进入京东(https://www.jd.com)后,我如果搜索特定的手机产品,如oppo find x2,会先出现如下的商品列表页: 如果点击进入其中一个商品会进入到如下图所示的商品详情页,可以看到用 ...

  2. 实验3ss

    1.实验任务1 #include <math.h> #include <stdio.h> int main() { float a,b,c,x1,x2; float delta ...

  3. linux nf_conntrack 连接跟踪机制 3-hook

    conntrack hook函数分析 enum nf_ip_hook_priorities { NF_IP_PRI_FIRST = INT_MIN, NF_IP_PRI_CONNTRACK_DEFRA ...

  4. 11.java设计模式之享元模式

    基本需求: 小型的外包项目,给客户A做一个产品展示网站,客户A的朋友感觉效果不错,也希望做这样的产品展示网站,但是要求都有些不同 每个客户要求发布的方式不一样,A要求以新闻的方式发布,B要求以博客的方 ...

  5. TensorFlow_笔记

    Tensorflow 1.基本概念 TensorFlow是一个编程系统,使用图(graphs)来表示计算任务,图(graphs)中的节点称之为op(operation),一个op获得0个或多个Tens ...

  6. sqlilab less11-less18

    less-11 uname和passwd直接带入查询,万能密码 sqlmap自动搜索表单,或者抓包后用-r参数 less-12 post数据用小括号进行包裹,构造万能密码") or 1=1 ...

  7. 【Camtasia教学】如何添加光标效果

    随着网络技术的快速发展,手机等移动工具越来越普及,我们的生活也发生了很大的变化,例如我们以前必须去到学校才能学习知识,但是现在躺在床上都可以看国外的教学视频.所以在网上录制教学或者演示视频变得越来越常 ...

  8. 工作中用到的redis操作

    del exists 1.字符串 set,get 2.列表 lRange lRem lPush rPush 3.有序列表 zadd zrem zscore 4.hash hset hget hdel

  9. 早安打工人! 来把你的.NET程序模块化吧

    嗨朋友们,大家好! 还记得我是谁吗? 对了! 我就是 .NET 打工人 玩双截棍的熊猫 今天呐,我特别要向 写框架 的朋友们,想要写框架 ** 的朋友们,已经有框架** 的朋友问声好! 为什么呢?因为 ...

  10. 音乐制作:用FL Studio做电子音乐

    电音制作,自然少不了适合做电音的软件,市面上可以进行电音制作的软件不少,可是如果在这些软件中只能选择一款的话,想必多数人会把票投给FL Studio,毕竟高效率是永远不变的真理,今天就让我们来看看如何 ...