“土法炮制”之 OOM框架

一、什么是OOM框架?

  OOM 的全拼是 Object-Object-Map,意思是对象与对象之间的映射,OOM框架要解决的问题就是对象与对象之间数据的自动映射

  举一个具体的例子:用过MVC模式开发Web后台的小伙伴们都知道EO(Entity Object,实体对象)与DTO(Data Transfer Object,数据传输对象)之间需要进行一个转换。使用最原始的方法,我们会像这样去进行转换操作:

 1         /// <summary>
 2         /// EO类
 3         /// </summary>
 4         public class Student_EO
 5         {
 6             public string Id { get; set; }
 7
 8             public string Name { get; set; }
 9
10             public int Age { get; set; }
11         }
12
13         /// <summary>
14         /// DTO类
15         /// </summary>
16         public class Student_DTO
17         {
18             public string Id { get; set; }
19
20             public string StudentName { get; set; }
21
22             public int Age { get; set; }
23         }
24
25         /// <summary>
26         /// EO对象转DTO对象
27         /// </summary>
28         /// <param name="eo"></param>
29         /// <returns></returns>
30         public Student_DTO _toDTO(Student eo)
31         {
32             Student_DTO dto = new Student_DTO();
33             dto.Id = eo.Id;
34             dto.StudentName = eo.Name;
35             dto.Age = eo.Age;
36
37             return dto;
38         }
39                      

  看起来也不难嘛,这个代码很容易写。但是要注意的是,你的项目中可能有几十上百个EO类与DTO类需要进行这样的映射转换,作为一个懒惰的程序员这是最不能容忍的事情。那么,想偷懒,那就必须先动动脑子......

  于是,我们希望有一个框架能帮我们避免这枯燥乏味重复的代码,有一点想法的程序员很快就会想到:我们可以使用反射去获取类的字段将它们对应的去赋值。当然,我也是这么想,并且也是这么做的。于是废话不多说,开始动手,创造属于你自己的OOM框架吧,奥利给!!!!!

二、现成的OOM框架

  在即将自己动手实现OOM框架之前,当然是要先了解了解目前有哪些OOM框架在流行,毕竟你能想到的问题大部分人已经想到了,并且可能已经有了很好的解决方案,比如AutoMapper,EmitMapper...这些框架,不得不承认,这些轮子已经很好用了,它们已经提供了丰富而全面的功能,但是,这并不妨碍我探索的激情。最后我也会总结一下自己“土法炮制”的OOM框架与AutoMapper框架对比的情况。废话不多说,往下看。

三、自制OOM框架的需求分析及技术选择

  决定要做一件事情之前,你最好要清楚地知道你将完成的东西最后具体是什么样的,这样的一个好处是能理清你需要的功能点,再一个就是你要确定你想要的是不是这样一个东西。

  Ok,我现在想要做一个OOM框架,就把它叫做CoffeeMapper框架吧,那么我脑海里CoffeeMapper的模样是这样的:

  1、实现任意两个类的对象之间的属性的映射,默认相同属性名的属性之间进行映射,也可指定要映射的属性名

  2、可以自由设定映射的逻辑

  3、映射转换的速度尽可能的快

  其中第一条最好理解,那就是实现OOM框架最基本的功能,值的映射关系绑定,我们可以通过添加Attribute标签的方式进行绑定标注。

  第二条也很好理解,默认的映射逻辑是等值映射,也就是映射类与被映射类之间对应的属性值是相等的;自由设定映射逻辑也就是说我可以根据需要设置我自己的映射逻辑,比如:原来的值加个前缀或后缀再映射...

  最后一条是最模糊的,什么叫做映射转换的速度尽可能的快?那我们就要清楚,如果使用的是反射的方法先创建一个目标的对象,然后再逐个对对象的属性值进行赋值操作,那么你必须要知道的一点是利用反射去创建一个对象的性能是不理想的,就像这样子:

    Form1 form1 = Activator.CreateInstance(typeof(Form1)) as Form1;

  看大神关于反射性能对比的分析:https://www.cnblogs.com/7tiny/p/9861166.html再看ExpressionTree,Emit,反射创建对象性能对比

  那么如何避免这个性能的瓶颈呢?在这里我选择运用表达式树的技术来提升框架整体的性能。

四、源代码展示与思路分析

  代码也不多,就一起放在这了,方便小伙伴们查看

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
    public class MapperAttribute:Attribute
    {
        /// <summary>
        /// the Property Name that map to
        /// </summary>
        public string MapTo { get; set; }

        public MapperAttribute() { }
        public MapperAttribute(string mapTo)
        {
            this.MapTo = mapTo;
        }

        public static string GetMapToPropertyName(PropertyInfo propertyInfo)
        {
            object[] mapperAttrs = propertyInfo.GetCustomAttributes(typeof(MapperAttribute), false);

            MapperAttribute mapperAttr;

            if (mapperAttrs != null && mapperAttrs.Count() >= 1)
            {
                mapperAttr = mapperAttrs[0] as MapperAttribute;

                return mapperAttr.MapTo;
            }
            else
            {
                return propertyInfo.Name;
            }
        }
    }

MapperAttribute

1     [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
2     public class NoMapperAttribute:Attribute
3     {
4     }

NoMapperAttribute

 1     public static class TypeExtension
 2     {
 3         /// <summary>
 4         /// Get All level inherit class types of current type
 5         /// </summary>
 6         /// <param name="currentType"></param>
 7         /// <param name="outInheritClassList">storage result of type</param>
 8         public static void GetInheritClassTypes(Type currentType, ref List<Type> outInheritClassList)
 9         {
10             outInheritClassList.Add(currentType);
11
12             if (currentType.BaseType.Name != "Object")
13             {
14                 GetInheritClassTypes(currentType.BaseType, ref outInheritClassList);
15             }
16             else
17             {
18                 return;
19             }
20         }
21     }

TypeExtension

  1     public sealed class CoffeeMapper<TIn, TOut> where TIn:class where TOut:class
  2     {
  3         private static readonly Func<TIn, TOut> funcCache = FuncFactory();
  4         public static TOut AutoMap(TIn InData, Action<TOut, TIn> action = null)
  5         {
  6             TOut _out = funcCache(InData);
  7
  8             if (null != action) action(_out, InData);
  9
 10             return _out;
 11         }
 12         private static Func<TIn, TOut> FuncFactory()
 13         {
 14             #region get Info through Reflection
 15
 16             var _outType = typeof(TOut);
 17             var _inType = typeof(TIn);
 18             var _outTypeProperties = _outType.GetProperties(BindingFlags.Instance | BindingFlags.Public);
 19             var _outTypePropertyNames = _outTypeProperties.Select(p => p.Name);
 20
 21             #endregion
 22
 23             #region some Expression class that can be repeat used
 24
 25             //Student in
 26             var _inDeclare = Expression.Parameter(_inType, "_in");
 27             //StudentDTO _out
 28             var _outDeclare = Expression.Parameter(_outType, "_out");
 29             //new StudentDTO()
 30             var new_outEntityExpression = Expression.New(_outType);
 31             //default(StudentDTO)
 32             var default_outEntityValue = Expression.Default(_outType);
 33             //_in == null
 34             var _inEqualnullExpression = Expression.Equal(_inDeclare, Expression.Constant(null));
 35
 36             #endregion
 37
 38             var set_inEntityNotNullBlockExpressions = new List<Expression>();
 39
 40             #region _out = new StudentDTO();
 41             set_inEntityNotNullBlockExpressions.Add(Expression.Assign(_outDeclare, new_outEntityExpression));
 42             #endregion
 43
 44             PropertyInfo[] needMapPropertys = ScanAllPropertyNeedMap();
 45
 46             foreach (var propertyInfo in needMapPropertys)
 47             {
 48                 string mapToName = MapperAttribute.GetMapToPropertyName(propertyInfo);
 49
 50                 //no contain, no map
 51                 if (!_outTypePropertyNames.Contains(mapToName))
 52                     continue;
 53
 54                 //no type equal, no map and expection
 55                 if (_outTypeProperties.First(p => p.Name == mapToName).PropertyType.FullName != propertyInfo.PropertyType.FullName)
 56                     continue;
 57
 58                 if (propertyInfo.CanWrite)
 59                 {
 60                     //_out.Id
 61                     var _outPropertyExpression = Expression.Property(_outDeclare, _outTypeProperties.First(p => p.Name == mapToName));
 62                     //_in.Id
 63                     var _inPropertyExpression = Expression.Property(_inDeclare, propertyInfo);
 64
 65                     //_out.Id = _in.Id;
 66                     set_inEntityNotNullBlockExpressions.Add(
 67
 68                         Expression.Assign(_outPropertyExpression, _inPropertyExpression)
 69                     );
 70                 }
 71             }
 72
 73             var checkIf_inIsNull = Expression.IfThenElse(
 74                 _inEqualnullExpression,
 75                 Expression.Assign(_outDeclare, default_outEntityValue),
 76                 Expression.Block(set_inEntityNotNullBlockExpressions)
 77             );
 78
 79             var body = Expression.Block(
 80
 81                 new[] { _outDeclare },
 82                 checkIf_inIsNull,
 83                 _outDeclare   //return _out;
 84             );
 85
 86             return Expression.Lambda<Func<TIn, TOut>>(body, _inDeclare).Compile();
 87         }
 88
 89         /// <summary>
 90         /// Get All Property Info that need be mapped
 91         /// </summary>
 92         /// <typeparam name="TIn"></typeparam>
 93         /// <param name="InData"></param>
 94         /// <returns></returns>
 95         private static PropertyInfo[] ScanAllPropertyNeedMap()
 96         {
 97             List<PropertyInfo> propertyInfoList = new List<PropertyInfo>();
 98
 99             //取得包括當前層級類在內的所有繼承的每一層祖先類型
100             List<Type> inheritClassList = new List<Type>();
101             TypeExtension.GetInheritClassTypes(typeof(TIn), ref inheritClassList);
102
103             foreach (Type classType in inheritClassList)
104             {
105                 var attrs = classType.GetCustomAttributes(typeof(MapperAttribute), false);
106                 if (null == attrs || attrs.Count() <= 0) continue;
107
108                 PropertyInfo[] currentClassPropertyInfos = classType.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance)
109                                                                     .Where(proInfo => proInfo.GetCustomAttributes(typeof(NoMapperAttribute)).Count() <= 0)
110                                                                     .ToArray();
111                 propertyInfoList.AddRange(currentClassPropertyInfos);
112             }
113
114             return propertyInfoList.ToArray();
115         }
116
117         #region
118         //#region 實驗類型
119         //public class EntityBase
120         //{
121         //    /// <summary>
122         //    /// storage QueryField‘s Result
123         //    /// </summary>
124         //    private Dictionary<string, object> queryFieldsDictionary = new Dictionary<string, object>();
125
126         //}
127
128         //[Mapper]
129         //public class BaseEO : EntityBase
130         //{
131         //    public string id { get; set; }
132         //}
133         //[Mapper]
134         //public class Student : BaseEO
135         //{
136         //    [Mapper("studentName")]
137         //    public string name { get; set; }
138         //    public int age { get; set; }
139         //    [NoMapper]
140         //    public string nomapField { get; set; }
141         //}
142
143         //public class BaseDTO
144         //{
145         //    public string id { get; set; }
146         //}
147         //public class StudentDTO : BaseDTO
148         //{
149         //    public string studentName { get; set; }
150         //    public int age { get; set; }
151         //}
152         //#endregion
153
154         //public StudentDTO Student_AutoMapTo_StudentDTO(Student _in)
155         //{
156         //    StudentDTO _out;
157
158         //    if (_in == null)
159         //        _out = default(StudentDTO);
160         //    else
161         //    {
162         //        _out = new StudentDTO();
163
164         //        _out.id = _in.id;
165         //        _out.age = _in.age;
166         //        _out.studentName = _in.name;
167         //    }
168
169         //    return _out;
170         //}
171         #endregion
172
173     }

CoffeeMapper

  重要优化点思路:

  每一个映射类都会生成一个进行值映射的方法委托,并将其以类静态字段的方式缓存下来,除第一次需要执行表达式树生成映射方法之外,以后的每一次调用都是直接调用缓存下来的委托,其执行效率和直接执行一个方法的效率几乎是一样的,这就达到了映射转换的速度尽可能的快的要求。

  现在,我们可以看一下如何使用“土炮”—CoffeeMapper进行对象之间的属性值映射:

 1     public class EntityBase
 2     {
 3     }
 4
 5     [Mapper]
 6     public class BaseEO:EntityBase
 7     {
 8         public string id { get; set; }
 9     }
10
11     [Mapper]
12     public class Student:BaseEO
13     {
14         [Mapper("studentName")]
15         public string name { get; set; }
16         public int age { get; set; }
17     }
18
19     public class BaseDTO
20     {
21         public string id { get; set; }
22     }
23     public class StudentDTO:BaseDTO
24     {
25         public string studentName { get; set; }
26         public int age { get; set; }
27     }
28
29     class Program
30     {
31
32         static void Main(string[] args)
33         {
34             Student s = new Student { id = "123456", name = "wuqiansen", age = 18 };
35        //一行代码实现映射
36             StudentDTO t = CoffeeMapper<Student, StudentDTO>.AutoMap(s, (t1, t2)=> { t1.studentName = t2.name+"default"; });
37         }
38
39     }

  我们看到程序中使用了一行代码就完成了对象之间的属性值映射,程序员的头发又可以少掉几根了!!!!

五、总结收获

  通过这次OOM框架的造轮子,更加熟练了表达式树技术的使用。这是一个非常简单的轮子,其实与AutoMapper对比起来,这个框架就显得过于简单了,还有很多需要提高和完善的地方:比如,只可以进行扁平类之间的映射、只实现了属性的映射、只实现了一对一的类对象映射。但是,优点也恰恰是来源于此,由于它功能的简单,所以它能在满足日常开发需求的前提下达到轻量级、性能优秀的要求。最重要的还是在这个过程中学到了很多!!!!!

原文地址:https://www.cnblogs.com/MaMaNongNong/p/12173620.html

时间: 2024-10-09 22:59:02

“土法炮制”之 OOM框架的相关文章

OOM框架AutoMapper基本使用(1)

OOM顾名思义,Object-Object-Mapping实体间相互转换,AutoMapper也是个老生常谈了,其意义在于帮助你无需手动的转换简单而又麻烦的实体间关系,比如ViewModel和entity的转换,SearchModel和Entity的转换,我这篇分享的意义在于,网上大多数的分享都是几年前的,很多方法已经被废弃,到了编译器里会告诉你该方法已经过时,废弃的,不建议使用的,比如Mapper.CreateMap等方法,当然老司机大多数直接就去github看文档了,或者google一下就了

OOM框架AutoMapper基本使用(2)

出于安全考虑,在后台与前台进行数据传输时,往往不会直接传输实体模型,而是使用Dto(Data transfer object 数据传输对象),这样在后台往前台传递数据时可以省略不必要的信息,只保留必要的信息,大大增强数据安全性. 下面给出两个相互对应的关系模型User.UserDto public class User { private const int NameMaxLength = 20; private const int PassWordMaxLength = 16; [Key] p

android Sqlite操作之-- 自定义ORM关系实体映射类

任何android应用程序都少不了数据库的操作,即使是客户端程序也会有一些特定的数据存入到数据库中,例如:用户浏览记录,收藏列表等等,所以数据库的操作就是一个会很频繁使用的操作,所以对这个部分的封装就很有必要了,Web端有Hibernate等一系列优秀的框架,虽然android应用程序在git上也有一些开源的OOM框架,但总觉得还是没必要引入第三方的东西,于是就自己封装了一个数据库操作类,只要调用此类相应的方法,传入要保存的实体对象或更新的实体对象即可,查询也是同样的,只要传入查询条件和Clas

(五).NET Core中过滤器Filter的使用介绍

知识点回顾: 前面几篇文章分别给大家介绍了 (1)Swagger的集成和用法: (2)JWT身份验证的集成和用法: (3)OOM框架AnutoMapper对象映射的用法: 今天给大家介绍过滤器Filter的基本使用: 过滤器有什么作用,在什么场景下适合用到它? 假设一个项目进展到快结束的时候,项目leader为了保证程序的稳定性和可监控和维护性要求将所有的方法加上日志,如果项目比较庞大,方法非常多,那岂不是得费很大得劲来完成这样一件事情.不过不用担心,咋们遇到的问题,伟大的语言设计者早已帮我们想

ImageLoad(三级缓存(NetCache,MemoryCache,DiskCache),开源框架,OOM)

ImageLoad 原创作品:未经本人允许,不得转载 前段时间写项目时遇到了一个问题,就是从网络获取图片资源的问题,总是出现OOM异常,经过几天的努力,终于处理的还算是可以使用,OOM的处理一直都是很头疼的问题.对于三级缓存的处理也是每个项目所必不可少的,所以我打算把我的学习心的写下来,以下主要针对三级缓存的原理,以及一些知识点进行详细的叙述,有不对的地方还希望大家能多加点评,指正 一.前言 首先再说三级缓存之前我们首先了解一下垃圾回收机制 下面是目前我所了解的几种引用关系 (一).强引用(St

Andorid Volley框架加载图片OOM问题分析

一.Volley框架简介 在这之前,我们在程序中需要和网络通信的时候,大体使用的东西莫过于AsyncTaskLoader,HttpURLConnection,AsyncTask,HTTPClient(Apache)等,Google 在2013年的I/O大会 上,发布了Volley.Volley是Android平台上的网络通信库,能使网络通信更快,更简单,更健壮. Volley提供了JsonObjectRequest.JsonArrayRequestStringRequest等Request形式

java 导出 excel 最佳实践,大文件 excel 避免OOM(内存溢出) 框架-02-API

项目简介 IExcel 用于优雅地读取和写入 excel. 避免大 excel 出现 oom,简约而不简单.. 特性 OO 的方式操作 excel,编程更加方便优雅. sax 模式读取,SXSS 模式写入.避免 excel 大文件 OOM. 基于注解,编程更加灵活. 写入可以基于对象列表,也可以基于 Map,实际使用更加方便. 设计简单,注释完整.方便大家学习改造. 变更日志 变更日志 v0.0.4 主要变化 引入 ExcelBs 引导类,优化使用体验. 创作缘由 实际工作和学习中,apache

框架--------异步下载图片(避免OOM)

一.简介 异步下载图片,图片保存在内存和SD卡中. 优点:简洁,用起来方便. 二.地址和支持的版本 下载地址:https://github.com/thest1/LazyList 再贴个地址:http://download.csdn.net/detail/mwj_88/7964883 支持版本:2.0+ 三.用法 定义一个ImageLoader实例,然后调用其DisplayImage()即可,最后记得清除缓存. [java] view plaincopy ImageLoader imageLoa

Android开源框架 Android-Universal-Image-Loader

Android开源框架Universal-Image-Loader就像图片加载守护者,为我们提供了丰富的功能特性: (1)多线程加载图像(异步或同步): (2)高度可定制化imageloader配置(线程池.图片下载器.解码器.内存和磁盘缓存.显示图像选项等): (3)每一个显示图像有许多自定义选项(存根图片,缓存开关,解码选项,位图处理和显示等): (4)支持内存和磁盘上的图像缓存(设备的文件系统和SD卡): (5)监听加载过程(包括下载进度): 下来我们详解如何配置使用Universal-I