在Web开发中常用的数据交换(赋值)中间件按照所对应的使用对象有两大类:

  1. 程序与数据库进行数据交换(赋值)中间件:Entity Framework (EF) Core、FluentMigrator。
  2. 程序与前台界面(UI)模型类进行数据交换(赋值)中间件:AutoMapper。

使用数据转换(赋值)中间件的N种理由

1、大型的工程性程序都是有多个擅长不同领域的开发者分工合作来完成的,擅长不同领域的开发者,把大型程序性程序进行拆分按照不同的层级模块进行开发。但在整个程序进行有机的整合时,由于不同层级的模块之间所进行数据交换时数据的定义不是完全相同的,为了保障整个程序能够在整合后可以正常的被执行,就需要通过不同应用场景的数据转换(赋值)中间件,来完成协助不同的层级模块之进行数据(赋值)的交换操作。

2、不同的层级模块之进行数据(赋值)的交换操作,当然开发者也可以不通过数据交换(赋值)中间件,由自己直接定义来完成,但你能保证自定义的代码比经过多个版本迭代的第3方数据转换(赋值)中间件功能更为强大、安全性更高,同时软件特别是一些实用性的软件都是工程性的产品,工程性的产品的1大特性就是集成现有的成熟性高的零部件,以最终形成1个相对低成本,功能性强大、安全性高的产品,特别是在软件行业随着开源和物联网的发展,这些因素结合起来,在自己开发的程序中调用大量的第3方中间件和第3方法软件来集成自己的程序将是今后应用程序主要的开发形式。

3、通过数据交换(赋值)中间件,交换(赋值)数据,是一种特殊形式上的数据清洗操作,只不过前者依赖于特定的中间件,后者依赖于特定的数据库软件。在工程性程序中集成大量的第3方中间件,也是“不要重复的制造轮子”这一基本软件工程指导性原则实际应用的具体体现。

注意:

在基于.Net(Core)框架中通过AutoMapper数据转交(赋值)中间件,自动实现数据交换(赋值)操作,首先必须通过“Nuget”引用:“AutoMapper.Extensions.Microsoft.DependencyInjection”

实体类(程序)与模型类(前台界面(UI))

/// <summary>

/// 【订单--类】

/// <remarks>

/// 摘要:

///     订单实体类,通过该实体类及其属性成员实现当前程序与数据库中订单表之间的数据交互操作。

/// </remarks>

/// </summary>

public class Order

{

/// <summary>

/// 【编号】

/// <remarks>

/// 摘要:

///    获取/设置1个指定订单的整型编号值。

/// </remarks>

/// </summary>

public int Id { get; set; }

/// <summary>

/// 【用户编号】

/// <remarks>

/// 摘要:

///    获取/设置1个指定订单相对应用户的整型编号值。

/// </remarks>

/// </summary>

public int CustomId { get; set; }

/// <summary>

/// 【名称】

/// <remarks>

/// 摘要:

///    获取/设置1个指定订单的名称。

/// </remarks>

/// </summary>

public string Name { get; set; }

/// <summary>

/// 【订单总价】

/// <remarks>

/// 摘要:

///    获取/设置1个指定订单的总价格。

/// </remarks>

/// </summary>

public decimal OrderTotal { get; set; }

/// <summary>

/// 【删除?】

/// <remarks>

/// 摘要:

///     获取/设置1个值false(不删除)/true(删除活),该值指示1个指定订单是否已经被逻辑删除。

/// </remarks>

public bool Deleted { get; set; }

/// <summary>

/// 【新建日期时间】

/// <remarks>

/// 摘要:

///    获取/设置1个指定订单被新建的时间值。

/// </remarks>

/// </summary>

public DateTime CreateTime { get; set; }

}

/// <summary>

/// 【订单项--类】

/// <remarks>

/// 摘要:

///   订单项实体类,通过该实体类及其属性成员实现当前程序与数据库中订单表项之间的数据交互操作。

/// </remarks>

/// </summary>

public class OrderItem

{

/// <summary>

/// 【项编号】

/// <remarks>

/// 摘要:

///    获取/设置1个指定订单项的整型编号值。

/// </remarks>

/// </summary>

public int ItemId { get; set; }

/// <summary>

/// 【订单编号】

/// <remarks>

/// 摘要:

///    获取/设置1个指定订单项相对应订单的整型编号值。

/// </remarks>

/// </summary>

public int OrderId { get; set; }

/// <summary>

/// 【名称】

/// <remarks>

/// 摘要:

///    获取/设置1个指定订单项的名称。

/// </remarks>

/// </summary>

public string Name { get; set; }

/// <summary>

/// 【数量】

/// <remarks>

/// 摘要:

///    获取/设置1个指定订单项的数量值。

/// </remarks>

/// </summary>

public decimal Quantity { get; set; }

/// <summary>

/// 【单价】

/// <remarks>

/// 摘要:

///    获取/设置1个指定订单项的单价。

/// </remarks>

/// </summary>

public decimal UnitPrice { get; set; }

/// <summary>

/// 【新建日期时间】

/// <remarks>

/// 摘要:

///    获取/设置1个指定订单项被新建的时间值。

/// </remarks>

/// </summary>

public DateTime CreateTime { get; set; }

}

/// <summary>

/// 【订单模型--类】

/// <remarks>

/// 摘要:

///     通过该类中的属性成员在程序执行时,用于当前程序和前台指定界面(UI)页面之间数据的交互和输入验证操作。

/// </remarks>

/// </summary>

public class OrderDTO

{

/// <summary>

/// 【编号】

/// <remarks>

/// 摘要:

///    获取/设置1个指定订单的整型编号值。

/// </remarks>

/// </summary>

public int Id { get; set; }

/// <summary>

/// 【用户编号】

/// <remarks>

/// 摘要:

///    获取/设置1个指定订单相对应用户的整型编号值。

/// </remarks>

/// </summary>

public int CustomId { get; set; }

/// <summary>

/// 【订单名称】

/// <remarks>

/// 摘要:

///    获取/设置1个指定订单的名称。

/// </remarks>

/// </summary>

public string OrderName { get; set; }

/// <summary>

/// 【订单总计】

/// <remarks>

/// 摘要:

///    获取/设置1个指定订单的总价格。

/// </remarks>

/// </summary>

public decimal OrderTotal { get; set; }

/// <summary>

/// 【新建日期时间】

/// <remarks>

/// 摘要:

///    获取/设置1个指定订单被新建的时间值。

/// </remarks>

/// </summary>

public DateTime CreateTime { get; set; }

}

/// <summary>

/// 【订单项模型--类】

/// <remarks>

/// 摘要:

///     通过该类中的属性成员在程序执行时,用于当前程序和前台指定界面(UI)页面之间数据的交互和输入验证操作。

/// </remarks>

/// </summary>

public class OrderItemDTO

{

/// <summary>

/// 【项编号】

/// <remarks>

/// 摘要:

///    获取/设置1个指定订单项的整型编号值。

/// </remarks>

/// </summary>

public int ItemId { get; set; }

/// <summary>

/// 【订单编号】

/// <remarks>

/// 摘要:

///    获取/设置1个指定订单项相对应订单的整型编号值。

/// </remarks>

/// </summary>

public int OrderId { get; set; }

/// <summary>

/// 【名称】

/// <remarks>

/// 摘要:

///    获取/设置1个指定订单项的名称。

/// </remarks>

/// </summary>

public string Name { get; set; }

/// <summary>

/// 【数量】

/// <remarks>

/// 摘要:

///    获取/设置1个指定订单项的数量值。

/// </remarks>

/// </summary>

public decimal Quantity { get; set; }

/// <summary>

/// 【单价】

/// <remarks>

/// 摘要:

///    获取/设置1个指定订单项的单价。

/// </remarks>

/// </summary>

public decimal UnitPrice { get; set; }

/// <summary>

/// 【新建日期时间】

/// <remarks>

/// 摘要:

///    获取/设置1个指定订单项被新建的时间值。

/// </remarks>

/// </summary>

public string CreateTime { get; set; }

}

public static List<OrderItem> GetOrderItemList()

{

OrderItem _orderItem_1 = new OrderItem

{

ItemId = 1,

OrderId = 1,

Name = "Intel 酷睿 i5 12400F",

Quantity = 1,

UnitPrice = 1499.00M,

CreateTime = DateTime.Now,

};

OrderItem _orderItem_2 = new OrderItem

{

ItemId = 2,

OrderId = 1,

Name = "Intel 酷睿 i7 12700KF",

Quantity = 2,

UnitPrice = 2949.00M,

CreateTime = DateTime.Now,

};

List<OrderItem> _orderItemList = new List<OrderItem> { _orderItem_1, _orderItem_2 };

return _orderItemList;

}

public static Order GetOrder(List<OrderItem> orderItemList)

{

decimal _orderTotal = 0;

foreach(var item in orderItemList)

{

_orderTotal += item.Quantity * item.UnitPrice;

}

Order _order_1 = new Order

{

Id = 1,

CustomId = 1,

Name = "测试",

OrderTotal = _orderTotal,

Deleted = false,

CreateTime = DateTime.Now,

};

return _order_1;

}

通过MapperConfiguration配置AutoMapper

通过MapperConfiguration泛型配置实现数据交换(赋值)操作

/// <summary>

/// 【映射扩展--类】

/// <remarks>

/// 摘要:

///    该类通过“AutoMapper”中间件,把1个指定类型的1/多个指定实例中的数据直接赋值到另1个指定类型的1/多个指定实例中。

/// 注意:

///     该类及其所有成员都被限定为静态。

/// </remarks>

/// </summary>

public static class MapExtensions

{

/// <typeparam name="TSource">泛型类型实例(1个指定的要被转化类的类型实例)。</typeparam>

/// <typeparam name="TDestination">泛型类型实例(这里主要指,1个指定的转化后类的类型实例)。</typeparam>

/// <param name="source">1个指定的要被转化的实体的1个实例。</param>

/// <summary>

/// 【映射到】

/// <remarks>

/// 摘要:

///     通过相应的参数实例及其“AutoMapper”中间件,把1个指定类型的1个指定实例中的数据直接赋值到另1个指定类型的1个指定实例中。

/// </remarks>

/// <returns>

/// 返回:

///    被赋值后的另1个指定类型的1个指定实例。

/// </returns>

/// </summary>

public static TDestination MapTo<TSource, TDestination>(this TSource source) where TSource : class where TDestination : class

{

if (source == null) return default(TDestination);

var config = new MapperConfiguration(cfg => cfg.CreateMap<TSource, TDestination>());

var mapper = config.CreateMapper();

return mapper.Map<TDestination>(source);

}

/// <typeparam name="TSource">泛型类型实例(1个指定的要被转化类的类型实例)。</typeparam>

/// <typeparam name="TDestination">泛型类型实例(这里主要指,1个指定的转化后类的类型实例)。</typeparam>

/// <param name="source">枚举数实例,该实例中存储着1个指定的要被转化的实体的多个实例。</param>

/// <summary>

/// 【映射到列表】

/// <remarks>

/// 摘要:

///    通过相应的参数实例及其“AutoMapper”中间件,把1个指定类型的多个指定实例中的数据直接赋值到另1个指定类型的多个指定实例中。

/// </remarks>

/// <returns>

/// 返回:

///    枚举数实例,该实例中存储着1个指定的要被转化的实体的多个实例。

/// </returns>

/// </summary>

public static IEnumerable<TDestination> MapToList<TSource, TDestination>(this IEnumerable<TSource> source) where TSource : class where TDestination : class

{

if (source == null) return new List<TDestination>();

var config = new MapperConfiguration(cfg => cfg.CreateMap<TSource, TDestination>());

var mapper = config.CreateMapper();

return mapper.Map<List<TDestination>>(source);

}

}

public IActionResult ExtensionsConfiguration()

{

OrderDTO _orderDTO = GetOrder(GetOrderItemList()).MapTo<Order,OrderDTO>();

return View(_orderDTO);

}

public IActionResult ExtensionsConfigurationList()

{

return View(GetOrderItemList().MapToList<OrderItem,OrderItemDTO>());

}

注意:  
由于数据转换(赋值)操作主要针对不完全相同的数据之间进行的(数据清洗),所以通过泛型操作来实现数据转换(赋值)操作,在实际实现中并不实用;在实际应用中最为实用的还是通过定制配置操作,来实现数据转换(赋值)操作。

如上图所示由于两个不同类中的属性成员的名称不完全相同,如果使用泛型的通用映射规则的配置,就会因数据转换操作不能进行相互映射,而导至“订单名称”无与之相对应的数据。

通过MapperConfiguration定制配置实现数据交换(赋值)操作

/// <summary>

/// 【映射定制--类】

/// <remarks>

/// 摘要:

///    该类通过“AutoMapper”中间件,把订单实体的1/多个实例中的数据直接赋值到订单模型类的1/多个指定实例中。

/// 注意:

///     该类及其所有成员都被限定为静态。

/// </remarks>

/// </summary>

public static class MapCustomized

{

/// <param name="order">订单实体的1个实例。</param>

/// <summary>

/// 【订单定制】

/// <remarks>

/// 摘要:

///    通过相应的参数实例及其“AutoMapper”中间件,把订单实体的1个实例中的数据直接赋值到订单模型类的1个指定实例中。

/// </remarks>

/// <returns>

/// 返回:

///    被赋值后的订单模型类的1个指定实例。

/// </returns>

/// </summary>

public static OrderDTO OrderCustomized(this Order order)

{

if (order == null) return default(OrderDTO);

var config = new MapperConfiguration(cfg => cfg.CreateMap<Order, OrderDTO>()

.ForMember(d => d.OrderName, o => o.MapFrom(o => o.Name)));//字段名称不一致将“Name”映射到“OrderName”。

IMapper _mapper = config.CreateMapper();

return _mapper.Map<OrderDTO>(order);

}

/// <param name="orderItemList">列表实例,该实例中存储着订单实体的多个实例。</param>

/// <summary>

/// 【订单项定制】

/// <remarks>

/// 摘要:

///    通过相应的参数实例及其“AutoMapper”中间件,把订单实体的多个实例中的数据直接赋值到订单模型类的多个指定实例中。

/// </remarks>

/// <returns>

/// 返回:

///     列表实例,该实例中存储着订单实体的多个实例。

/// </returns>

/// </summary>

public static List<OrderItemDTO> OrderItemCustomized(this List<OrderItem> orderItemList)

{

if (orderItemList == null) return new List<OrderItemDTO>();

var config = new MapperConfiguration(cfg => cfg.CreateMap<OrderItem, OrderItemDTO>()

.ForMember(d=>d.CreateTime, options => options.MapFrom(o => o.CreateTime.ToString("yyyyMMddHHmmssfff")))); //字段类型不一致将“DateTime”类型的数据映射并格式化为“String”类型的数据。

IMapper _mapper = config.CreateMapper();

return _mapper.Map<List<OrderItemDTO>>(orderItemList);

}

}  public IActionResult CustomizedConfiguration()

{

OrderDTO _orderDTO = GetOrder(GetOrderItemList()).OrderCustomized();

return View(_orderDTO);

}

public IActionResult CustomizedConfigurationList()

{

return View(GetOrderItemList().OrderItemCustomized());

}

通过继承Profile定制配置AutoMapper

/// <summary>

/// 【订单映射配置--类】

/// <remarks>

/// 摘要:

///     通过当前类所定义的定制映射规则,在订单类和订单项类与订单模型类和订单项型类之间的属性成员直接通过“AutoMapper”中间件,进行数据相互赋值操作时;

/// 如果两者的属性成员之间有差异时,使两者属性成员之间的数据相互赋值操作,得以正常的被执行。

/// </remarks>

/// </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()));

//全局映射:在“Order”映射到“OrderDTO”时,忽略“Order”中的“Deleted”属性成员。

((IProfileExpressionInternal)this).ForAllMaps((mapConfiguration, map) =>

{

if (typeof(Order).IsAssignableFrom(mapConfiguration.DestinationType))

{

map.ForMember(nameof(Order.Deleted), options => options.Ignore());

}

});

}

}

/// <summary>

/// 【格式转换--类】

/// <remarks>

/// 摘要:

///     如果两个类之间的属性成员直接通过“AutoMapper”中间件,进行数据相互赋值操作时;如果两者的属性成员之间数据类型不同且需要进行数据相互赋值操作时,

/// 通过当前类中所定义的方法成员,使两个不同数据类型的属性成员之间,正常执行数据相互赋值操作。

/// </remarks>

/// </summary>

public class FormatConvert : IValueConverter<DateTime, string>

{

/// <param name="sourceMember">1个将要被转换属性成员的时间类型的值。</param>

/// <param name="context">解决方案上下文实例。</param>

/// <summary>

/// 【转换】

/// <remarks>

/// 摘要:

///     如果两个类之间的属性成员直接通过“AutoMapper”中间件,进行数据相互赋值操作时;如果两者的属性成员之间数据类型不同且需要进行数据相互赋值操作时,

/// 通过该方法成员,使两个不同数据类型的属性成员之间,正常执行数据相互赋值操作。

/// </remarks>

/// <returns>

/// 返回:

///    被转换后的字符串类型的数据值。

/// </returns>

/// </summary>

public string Convert(DateTime sourceMember, ResolutionContext context)

{

if (sourceMember == DateTime.MinValue)

return DateTime.Now.ToString("yyyyMMddHHmmssfff");

return sourceMember.ToString("yyyyMMddHHmmssfff");

}

}

注意:

在基于.Net(Core)框架的程序中使用继承于Profile的类来定义定制的数据交换规则,必须先把该类(或该类所定义的程序集)依赖注入到内置依赖注入容器中,开发者可以选择其中的任意1种进行注入:

//通过数据交换映射规则配置文件类,直接注入。

builder.Services.AddAutoMapper(typeof(OrderMapperProfile));

//通过当前程序作用域中的所有程序集里面扫描AutoMapper的配置文件“OrderMapperProfile”,注入。

builder.Services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());

// 通过数据交换映射规则配置文件类“OrderMapperProfile”所定义的程序集的名称,注入

builder.Services.AddAutoMapper(Assembly.Load("AutoMapperWeb"));

private readonly IMapper _mapper;

public HomeController(IMapper mapper)

{

_mapper = mapper;

}

如果不注入直接执行交换操作就会出现逻辑异常:“InvalidOperationException: Unable to resolve service for type 'AutoMapper.IMapper' while attempting to activate 'AutoMapperWeb.Controllers.HomeController'.”

public IActionResult CustomizedProfile()

{

OrderDTO _orderDTO = _mapper.Map<OrderDTO>(GetOrder(GetOrderItemList()));

return View(_orderDTO);

}

public IActionResult CustomizedProfileList()

{

return View(_mapper.Map<List<OrderItemDTO>>(GetOrderItemList()));

}

对以上功能更为具体实现和注释见:22-05-19-049_AutoMapperWeb(AutoMapper深入理解)。

第49章 AutoMapper深入理解相关推荐

  1. 一篇文章助你理解Python3中字符串编码问题

    前几天给大家介绍了unicode编码和utf-8编码的理论知识,以及Python2中字符串编码问题,没来得及上车的小伙伴们可以戳这篇文章:浅谈unicode编码和utf-8编码的关系和一篇文章助你理解 ...

  2. 敏捷管理第三章《敏捷理解》

    敏捷管理第三章<敏捷理解> 敏捷理解,让你管理变得不平凡 大家对敏捷的理解都有差别,可谓仁者见仁,智者见智,话术如下: 敏捷就是压榨劳动力,多干活 敏捷就是经常加班,比以往加班变得更严重, ...

  3. 【第3版emWin教程】第49章 emWin6.x的AppWizard创建控件回调消息

    教程不断更新中:第3版emWin教程和ThreadX GUIX教程开工,双管齐下,GUIX更新至第28章,emWin更新至第50章(2021-10-01) - uCOS & uCGUI &am ...

  4. 并发编程专题——第一章(深入理解java内存模型)

    说到并发编程,其实有时候觉得,开发中真遇到这些所谓的并发编程,场景多吗,这应该是很多互联网的在职人员,一直在考虑的事情,也一直很想问,但是又不敢问,想学习的同时,网上这些讲的又是乱七八糟,那么本章开始 ...

  5. 说好的30天直播生死呢?--第三章 我所理解的打击感(一)

    转载请注明来自:http://blog.csdn.net/wjlpld 大家都知道一个格斗游戏的灵魂就是打击感,对于打击感,相信每个人都有自己的理解,下面就来说说我对打击感的理解吧,虽然年纪小了点,不 ...

  6. 第51章 Bootstrap-Fileinput深入理解

    已经上传文件的回显 BootstrapFileinput插件可以用与已经上传到服务器端(图片)文件的回显,但是这并不是对回显操作的一个好的解决方案,对于对回显操作而言最好不要使用BootstrapFi ...

  7. 第15章-输入/输出 --- 理解Java的IO流

    (一)理解Java的IO流 JAVA的IO流是实现输入/输出的基础,它可以方便地实现数据的输入/输出操作,在Java中把不同的输入/输出(键盘.文件.网络连接等)抽象表述为"流"( ...

  8. 第 3 章 Keystone - 018 - 理解 Keystone 核心概念

    Keystone核心概念 作为 OpenStack 的基础支持服务,Keystone 做下面这几件事情: 1.管理用户及其权限 2.维护 OpenStack Services 的 Endpoint 3 ...

  9. java逻辑第九章_深入理解jvm-(第九章)类加载及执行子系统的案例与实战

    转载自:http://blog.csdn.net/coslay/article/details/49564789 概述 在Class文件格式与执行引擎这部分中,用户的程序能直接影响的内容并不太多, C ...

最新文章

  1. connect连接oracle6,Oracle Connect By 使用实例
  2. 在 Java 中,如何批量读取本项目资源目录下的所有文件
  3. HDLBits答案(14)_Verilog有限状态机(1)
  4. SimpleDateFormat解析的时候字符串过长问题
  5. 自然数,实数,有理数,整数,分数,无理数
  6. android 手机获取公网ip_KSWEB 旧手机搭建网站服务
  7. 他无意间玩了这12个游戏,却掌握了Python基础,前后只用了一个月
  8. alexa api php,php 读取 alexa信息
  9. 成功运行官方Tensorflow Android的demo的过程
  10. deinstall oracle 11g on linux
  11. 用“归并”改进“快速排序”
  12. CAnimation-模拟时钟
  13. Hadoop 删除节点步骤
  14. 【1】PASCAL VOC数据集下载
  15. 中国麻将:世界上最早的区块链项目
  16. 采访:蔡学镛谈复杂事务处理(CEP)
  17. 股权转让要交哪些税?增值税、企业所得税、个人所得税
  18. Like My Mother Always Said… by Erin McHugh
  19. URL Schemes
  20. 不错的U3D第一人称射击类游戏教程

热门文章

  1. Git Flow 分支管理简述
  2. Istio1.12:安装和快速入门
  3. php编程轻薄本,轻薄本好用,但接口太少,需要HUB来发挥功能
  4. 我国中小学生linux启蒙教育起步了吗?
  5. 2020年统考计算机试题软件,2020年计算机软件水平考试综合练习及参考答案
  6. IDEA自带maven报错maven-default-http-blocker (http://0.0.0.0/): Blocked mirror for repositories解决
  7. aarch64架构的linux安装gcc
  8. v74.01 鸿蒙内核源码分析(编码方式篇) | 机器指令是如何编码的 | 百篇博客分析OpenHarmony源码
  9. python画画100行代码_简单几步,100行代码用Python画一个蝙蝠侠的logo
  10. java获取随机数范围