Github 地址:https://github.com/iccb1013/Sheng.Mapper
原文:https://blog.shengxunwei.com/home/post/bde984a5-5c13-4667-ae8e-0e1a31e6bc2a

对象属性值映射/拷贝工具。不需要创建映射规则,不要求对象类型一致,适用于简单直接的拷贝操作,可以全属性拷贝,指定属性拷贝,排除指定的属性。拷贝包含 10 个属性的对象 10 万次,耗时 4.x 秒(普通开发机)。

  • 拷贝行为只针对 sourceObject 和 targetObject 所共有的属性
  • 在 sourceObject 和 targetObject 中的待拷贝的属性值的类型处理:如果是值类型,直接拷贝,如果是引用类型,sourceObject 中的属性的类型 必须 和 targetObject 中的属性的类型一致,或是它的派生类
  • 如果要支持类型不一致的属性自动进行类型转换,你可以在 PropertyMappingDescription 这个类中实现转换器功能
  • 拷贝行为 不会 改变 targetObject 中不需要被拷贝的属性的值
  • 你可以组合使用几个方法来从多个对象中拷贝指定的属性值到一个 targetObject

和 AutoMapper 互补,与之相比最大优势是短,平,快。不需要创建复杂的映射规则,并支持属性排除操作。

具体实现:

这里在具体实现上,其实并不复杂,只需对反射操作稍有了解即可,我们通过 sourceObject 和 targetObject ,获取它们的“类型(Type)”,然后使用 Type.GetProperties() 方法,获取这个对象类型所包含的属性(Property)。

PropertyInfo[] propertyList = Type.GetProperties();
foreach (PropertyInfo property in propertyList)
{PropertyMappingDescription propertyMappingDescription = new PropertyMappingDescription(property);_propertyList.Add(propertyMappingDescription);_propertyNames.Add(property.Name, propertyMappingDescription);
}  

这里有另外一个细节需要留意的是,我们要把同样类型(Type)的相关信息,缓存起来,这样下次再拷贝相同类型的对象时,就无需再去反射它的 Properties。

我们通过 TypeMappingDescription 对对象的类型信息进行缓存和包装,提供我们所需要的基本操作:

public bool ContainsProperty(string name)
{if (String.IsNullOrEmpty(name))throw new ArgumentNullException("TypeMappingDescription.ContainsProperty 必须指定属性名。");return _propertyNames.ContainsKey(name);
}
public object GetValue(object obj, string propertyName)
{if (obj == null)throw new ArgumentNullException("指定的对象为空。");if (obj.GetType() != this.Type)throw new ArgumentException("指定的对象类型与缓存的对象类型不一致。");if (_propertyNames.ContainsKey(propertyName) == false)throw new ArgumentOutOfRangeException("指定的属性名不存在。");PropertyMappingDescription propertyMappingDescription = (PropertyMappingDescription)_propertyNames[propertyName];if (propertyMappingDescription.CanRead == false)throw new InvalidOperationException("属性 " + propertyName + "不可读。");return propertyMappingDescription.GetValue(obj);
}
public void SetValue(object obj, string propertyName, object value)
{if (obj == null)throw new ArgumentNullException("指定的对象为空。");if (obj.GetType() != this.Type)throw new ArgumentException("指定的对象类型与缓存的对象类型不一致。");if (_propertyNames.ContainsKey(propertyName) == false)throw new ArgumentOutOfRangeException("指定的属性名不存在。");PropertyMappingDescription propertyMappingDescription = (PropertyMappingDescription)_propertyNames[propertyName];if (propertyMappingDescription.CanWrite == false)throw new InvalidOperationException("属性 " + propertyName + "只读。");Type propertyType = propertyMappingDescription.PropertyInfo.PropertyType;if (propertyType.IsValueType == false && value != null){Type valueType = value.GetType();if (propertyType != valueType && valueType.IsSubclassOf(propertyType) == false){throw new ArgumentException("目标对象的 " + propertyName + "与 value 的类型既不一致,也不是目标类型的派生类。");}}propertyMappingDescription.SetValue(obj, value);
}

同时我们使用 PropertyMappingDescription 对 PropertyInfo 进行封装。对 PropertyInfo 进行封装,是为了方便我们后续针对属性添加属性值的转换器,以便实现稍复杂一些的属性拷贝操作。

最后我们来测试一下拷贝操作:

A a = new A()
{Name = " 张三",Age = 10,Class = "一班",CObject = new SubC(){Message = " Hello";},P1 = "1",P2 = "2",P3 = "3",P4 = "4",P5 = "5",P6 = "6"
};
B b = new B();
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
for (int i = 0; i < 100000; i++)
{//全部属性拷贝ShengMapper.SetValues(a, b);//拷贝指定的属性// ShengMapper.SetValuesWithProperties(a, b, new string[] { "Name", "Age", "P1" });//排除指定的属性//ShengMapper.SetValuesWithoutProperties(a, b, new string[] { "Name", "Age", "P1" });}
stopwatch.Stop();
Console.WriteLine("对包含 10 个属性的对象的属性值拷贝 10 万次,耗时:" + stopwatch.Elapsed.ToString());
Console.ReadLine();

我模拟了一几个类,他们有不同类型的属性,还包括引用类型的属性我派生类。

对于包含 10 个属性的类的 10 万次属性值拷贝,在开发机上大约用了 4.x 秒。

下面直接用几个例子来具体的讲一下它的使用场景,和为什么要使用它,以及和 AutoMapper 的互补使用。

AutoMapper 是一款十分强大的对象映射工具,我在项目中大量的使用了 AutoMapper 这一工具,如处理 DTO 与 实体对象间的映射关系,使用类似如下代码:

但是在使用 AutoMapper 的过程中,有一些小细节会比较麻烦,它不能创建相同对象类型间的映射赋值关系,也不能根据场景为相同的对象映射关系创建不同的映射逻辑,所以我们目前的使用集中的 Dto 与 实体对象间的映射。

我们现在的项目使用了 Entity Framework ,在更新数据时,一般的逻辑是前端传过来一个 DTO 对象,转换成实体对象,根据 Id 去 DBContext 中拿到数据库中的实体对象,再用传入的实体对象给数据库中的对象赋值,SaveChanges。

类似如下代码,用传入的 storehouseBase 给 dbStorehouseBase 赋值:

这里如果使用 AutoMapper 就涉及到两个问题:

1.AutoMapper 是不能为相同的对象类型创建映射规则的,如:

2.如果不使用规则,即使可以直接 Map,实体对象中又存在大量的导航属性,是不能直接覆盖过去的,这就比较麻烦了,我们的项目中开发人员只好手工的用传入的实体对象,一个属性一个属性为数据库中拿到的实体对象赋值,如果要更新的属性比较多,就非常麻烦,此外在修改时给实体对象新增加了属性,是否还能保证记得回到这里来给添加新的赋值代码。

所以在这种场景下, 可以使用 ShengMapper 来解决:

ShengMapper 的 SetValuesSkipVirtual 方法,见文知义,它自动映射两个对象,进行赋值操作,并自动的跳过 Virtual 属性,因为 Entity Framework 的导航属性都是 Virtual 的(DB First)。

此外,还有一些更新赋值场景,存在一些特定的业务逻辑,比如说我更新用户时,不更新 Password 字段,不更新最后登录时间字段,更新订单时,不更新创建时间字段等等,这种同一种对象类型的映射赋值,不但要跳过导航属性,还要跳过指定的字段,也可以使用 ShengMapper 很好的解决,使用类似如下代码:

它的方法原型是:

SetValuesWithoutProperties 方法 把 product 中的属性,更新到 dbProduct 中,同时跳过 Supplier 属性和所有的 Virtual 属性。

那么既然能跳过指定的属性,自然我们也能只更新指定的属性,如:

SetValuesWithProperties 方法把 productEntryBatch 中的属性值更新到 dbProductEntryBatch 中,但是只更新 Name 这个属性。

ShengMapper 还提供了一些其它方法重载:

使用 ShengMapper 是不需要事先创建规则的,所有对对象的映射赋值操作,都是以更灵活的方式来完成的,比如我这一次 User 对 User 要排除 Password ,另一个场景不需要排除,也是可以的,而 AutoMapper 的规则必须是全局唯一的,一旦创建了 UserDTO 对 User 的规则,在所有的时候,都会遵循事先定义的规则去执行。

所以在实际应用中,可以通过与 AutoMapper 互补的方式,解决这方面的问题。

Github 开源:高效好用的对象间属性拷贝工具 升讯威 Mapper:( Sheng.Mapper)相关推荐

  1. Github 开源:使用升讯威 Mapper( Sheng.Mapper)与 AutoMapper 互补,大幅提高开发效率!...

    Github 地址:https://github.com/iccb1013/Sheng.Mapper 在上一篇幅中,简单介绍了 升讯威 Mapper( Sheng.Mapper)[http://www ...

  2. Github 开源:升讯威 Winform 开源控件库( Sheng.Winform.Controls)

    Github 地址:https://github.com/iccb1013/Sheng.Winform.Controls 本控件库中的代码大约写于10年前(2007年左右),难免有不成熟与欠考虑之处, ...

  3. GitHub开源:升讯威微信营销系统(第三方微信平台)完整源代码

    GitHub :https://github.com/iccb1013/Sheng.WeixinConstruction 升讯威微信营销系统开发实践系列 升讯威微信营销系统开发实践:(1)功能设计与架 ...

  4. 升讯威微信营销系统开发实践:(4)源代码结构说明 与 安装部署说明( 完整开源于 Github)...

    GitHub:https://github.com/iccb1013/Sheng.WeixinConstruction因为个人精力时间有限,不会再对现有代码进行更新维护,不过微信接口比较稳定,经测试至 ...

  5. 升讯威微信营销系统开发实践:(3)功能介绍与此项目推广过程的一些体会( 完整开源于 Github)...

    GitHub:https://github.com/iccb1013/Sheng.WeixinConstruction因为个人精力时间有限,不会再对现有代码进行更新维护,不过微信接口比较稳定,经测试至 ...

  6. 升讯威微信营销系统开发实践:订阅号和服务号深入分析( 完整开源于 Github)...

    GitHub:https://github.com/iccb1013/Sheng.WeixinConstruction因为个人精力时间有限,不会再对现有代码进行更新维护,不过微信接口比较稳定,经测试至 ...

  7. 升讯威微信营销系统开发实践:(1)功能概要与架构设计( 完整开源于 Github)...

    GitHub:https://github.com/iccb1013/Sheng.WeixinConstruction因为个人精力时间有限,不会再对现有代码进行更新维护,不过微信接口比较稳定,经测试至 ...

  8. GitHub开源:升讯威 SQLite 增强组件 Sheng.SQLite.Plus

    Github:https://github.com/iccb1013/Sheng.SQLite.Plus Sheng.SQLite.Plus 是一个对直接使用 ADO.NET 方式操作 SQLite ...

  9. 非常易于理解‘类'与'对象’ 间 属性 引用关系,暨《Python 中的引用和类属性的初步理解》读后感...

    关键字:名称,名称空间,引用,指针,指针类型的指针(即指向指针的指针) 我读完后的理解总结: 1. 我们知道,python中的变量的赋值操作,变量其实就是一个名称name,赋值就是将name引用到一个 ...

最新文章

  1. [Silverlight入门系列]实现局部元素全屏(Element部分全屏)
  2. 连接 insance 到 vlan101 - 每天5分钟玩转 OpenStack(97)
  3. 低噪声放大器和高功放的区别
  4. java通过按钮打开新窗口_如何在java程序中,当点击一个按钮后,关闭当前窗口,开启一个新的窗口。...
  5. SpringBoot零基础入门指南--搭建Springboot然后能够在浏览器返回数据
  6. Spring:Spring相关知识介绍笔记
  7. 基本的EJB参考,注入和查找
  8. springmvc ajax 页面无法重定向问题!!!!
  9. 信息学奥赛一本通 1242:网线主管 | OpenJudge NOI 1.11 04:网线主管
  10. countif函数比较两列不同_这些Excel函数公式,职场办公天天用,赶紧掌握!
  11. 朋友易得 ,知已难求
  12. 2019牛客多校第四场 I题 后缀自动机_后缀数组_求两个串de公共子串的种类数
  13. python怎么读取csv文件-python3读取csv文件任意行列代码实例
  14. Linux网络配置与远程连接
  15. 现学活用的XPath爬取豆瓣音乐
  16. vb6 由于超出容量限制 不能创建新事务_Executors类创建四种常见线程池
  17. android 入门-git之上传本地代码到github
  18. android超级终端使用,android系统超级终端怎么用?
  19. python库之airtest和pocoui
  20. 无需U盘,30秒找回win10/win7用户密码

热门文章

  1. 三分钟彻底禁用、隐藏Android设备底部虚拟按钮(亲测有效)
  2. csdn中的markdown编辑器如何快速复制粘贴图片?
  3. aida64使用方法_AIDA64怎么用 AIDA64使用教程与方法简介
  4. 蓝桥杯 算法提高 回文串
  5. HBase2.x(十六) HBase与 Hive 的集成
  6. composer 安装阿里云sdk
  7. 免费使用Camtasia S-tudio录屏剪辑制作视频微课
  8. asp.net987-超市会员管理系统#毕业设计
  9. 字符串转化为 List 集合
  10. MSTAR数据集下载地址及处理方法