【前言】

OOM框架想必大家在Web开发中是使用频率非常之高的,如果还不甚了解OOM框架,那么我们对OOM框架稍作讲解。

OOM顾名思义,Object-Object-Mapping实体间相互转换。常见的使用场景有两个实体要通过DTO对象进行页面的渲染,那么我们就需要通过对DTO对象的一个一个属性进行赋值,最终返回。整个过程是单调又繁琐的,甚至严重影响了代码的整洁性。更有强迫症高度患者可能看着这一坨shi一样的代码阵阵痉挛...因此,我们就想,能不能用一种简介的方法进行自动映射两个,三个甚至更多的对象,避免写这么一些低级乏味的代码。

于是乎,一大波一大波的自动映射框架悠然而生。业内比较出名的有:AutoMapper,EmitMapper,NLiteMapper,TinyMapper等。在这里,先抛去他们不谈,毕竟未必个人觉得好用,或者我就喜欢造轮子。本篇将讲解一波自己造的轮子OOM框架AutoMapper,并且有造轮子过程中的持续优化,我想,这便是学习的最好过程吧。

本文讲解了反射方式的实现方法以及后续抛弃反射的优化方法Expression Tree 表达式树的实现方法。

【实现思路】

  AutoMapper既然是OOM框架,那么就离不开Object,Object必然又涉及到类型。那么,泛型是必不可少的。

  框架要简洁易用,并且能兼容多数场景。于是,我们准备从以下几方面进行设计:

  1. 支持自动映射,能通过调用一个方法自动映射实体;
  2. 支持特殊属性特殊处理的扩展功能,特殊处理的部分可以特殊进行赋值;
  3. 有不想进行映射的属性能够进行屏蔽;
  4. 映射的名称要支持配置(有名字不同的场景无法自动映射,手动配置映射关系进行映射);
  5. 配置不能加配置文件,通过标签的方式(约定优于配置);
  6. 性能要卓越;
  7. 代码要通用,版本支持度要高(使用.net standard类库,可同时支持.net framework 和 .netcore);

【实现过程】

  项目结构:

  

  依据上面的思路,第一时间想到的便是通过反射获取到第一个对象的属性值,并动态创建第二个对象,遍历属性赋值,再添加细节处理。SoEasy!于是便兴冲冲完成了第一版。

  首先构造了几个特性标签类,用于标注是否要映射,以及映射的自定义别名的配置。

  DoNotMapperAttribute:不进行映射该属性

  MapperAttribute:添加自定义名称的映射配置

  MapperClassAttribute:该类支持映射(目前没有实际应用)

 using System;

 namespace SevenTiny.Bantina.AutoMapper
{
[AttributeUsage(AttributeTargets.Property, Inherited = true)]
public class DoNotMapperAttribute :Attribute
{
}
}
 using System;
using System.Linq;
using System.Reflection; namespace SevenTiny.Bantina.AutoMapper
{
[AttributeUsage(AttributeTargets.Property, Inherited = true)]
public class MapperAttribute : Attribute
{
public string TargetName { get; set; } public MapperAttribute() { }
public MapperAttribute(string targetName)
{
this.TargetName = targetName;
} public static string GetTargetName(PropertyInfo property)
{
var attr = property.GetCustomAttributes<MapperAttribute>(true).FirstOrDefault();
return attr != null ? (attr as MapperAttribute).TargetName ?? default(string) : default(string);
}
}
}
 using System;
using System.Linq; namespace SevenTiny.Bantina.AutoMapper
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Field, Inherited = true)]
public class MapperClassAttribute : Attribute
{
public string Name { get; set; }
public static string GetName(Type type)
{
var attr = type.GetCustomAttributes(typeof(MapperClassAttribute), true).FirstOrDefault();
return attr != null ? (attr as MapperClassAttribute).Name ?? default(string) : default(string);
}
}
} 

第一版反射实现代码:

 public sealed class Mapper
{
private Mapper() { }
/// <summary>
/// Init Source Value Dic
/// </summary>
/// <typeparam name="TSource"></typeparam>
/// <param name="source"></param>
/// <returns></returns>
private static Dictionary<string, object> Initdic<TSource>(TSource source)
{
Dictionary<string, object> dic = new Dictionary<string, object>();
foreach (PropertyInfo property in typeof(TSource).GetProperties())
{
string targetPropertyName = MapperAttribute.GetTargetName(property);
if (!string.IsNullOrEmpty(targetPropertyName))
{
if (!dic.ContainsKey(targetPropertyName))
{
dic.Add(targetPropertyName, property.GetValue(source));
}
}
else if (!dic.ContainsKey(property.Name))
{
dic.Add(property.Name, property.GetValue(source));
}
}
return dic;
}
/// <summary>
/// SetValue from propertyinfo and sourceDictionary
/// </summary>
/// <typeparam name="TValue"></typeparam>
/// <param name="value"></param>
/// <param name="propertyInfos"></param>
/// <param name="sourceDic"></param>
/// <returns></returns>
private static TValue SetValue<TValue>(TValue value, PropertyInfo[] propertyInfos, Dictionary<string, object> sourceDic, Dictionary<string, string> keys) where TValue : class
{
foreach (PropertyInfo property in propertyInfos)
{
if (!keys.ContainsKey(property.Name))
{
if (sourceDic.ContainsKey(property.Name))
{
try
{
property.SetValue(value, sourceDic[property.Name]);
keys.Add(property.Name, string.Empty);
}
catch (Exception)
{
property.SetValue(value, null);
}
}
}
}
return value;
}
/// <summary>
/// AutoMapper
/// </summary>
/// <typeparam name="TValue">value type</typeparam>
/// <typeparam name="TSource">source type</typeparam>
/// <param name="source"></param>
/// <returns></returns>
public static TValue AutoMapper<TValue, TSource>(TSource source) where TValue : class where TSource : class
{
TValue value = Activator.CreateInstance<TValue>();
PropertyInfo[] propertyInfos = typeof(TValue).GetProperties();
Dictionary<string, string> keys = new Dictionary<string, string>();
value = SetValue(value, propertyInfos, Initdic(source), keys);
return value;
}
/// <summary>
/// AutoMapper,Support for Use Action to custom special fields.
/// </summary>
/// <typeparam name="TValue"></typeparam>
/// <typeparam name="TSource"></typeparam>
/// <param name="source"></param>
/// <param name="action"></param>
/// <returns></returns>
public static TValue AutoMapper<TValue, TSource>(TSource source, Action<TValue> action) where TValue : class where TSource : class
{
TValue value = Activator.CreateInstance<TValue>();
PropertyInfo[] propertyInfos = typeof(TValue).GetProperties();
Dictionary<string, string> keys = new Dictionary<string, string>();
value = SetValue(value, propertyInfos, Initdic(source), keys);
action(value);
return value;
}
}

由于思路清晰,实现起来也是很容易的。

通过传入两个泛型(一个输入,一个输出),然后获取到其中一个的属性,并遍历另一个的属性,如果相同,则直接赋值(当然有特性标签部分的细节处理)。

由于有些属性已经赋值过了,再次遇到会重新扫描赋值,降低了性能和影响了逻辑性,这里做了Dictionary的暂时缓存,以便扫描过的值不再进行赋值。

特殊的值,我们使用了Action<T> action 匿名委托的方式进行赋值,提供了特殊处理的能力。

我们下面进行一波使用,简单封装一个多次调用的测试方法:

 public static TimeSpan Caculate(int executTimes, Action action)
{
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = ; i < executTimes; i++)
{
action();
}
sw.Stop();
return sw.Elapsed;
}

简单写一个控制台应用程序,并传入参数100万次调用,并打印执行用时:

 Student1 stu1 = new Student1 { Uid = Guid.NewGuid() };
Student5 stu5 = new Student5 { HealthLevel = , SchoolClass = new SchoolClass { Name = "class1" } }; var test1 = StopwatchHelper.Caculate(, () =>
{
Student stu = Mapper.AutoMapper<Student, Student5>(stu5, t => t.Name = "jony");
});
Console.WriteLine(test1.TotalMilliseconds);

执行100w次大概需要3654毫秒,也就是3.6秒。

虽说100万次小程序是可以忽略的,但是对于高并发的场景100万次再平常不过了,这个性能是不能忍受的!

再看一眼直接调用的性能:

 Student5 stu5 = new Student5 { HealthLevel = , SchoolClass = new SchoolClass { Name = "class1" } };

 var test1 = StopwatchHelper.Caculate(, () =>
{
Student stu = new Student { HealthLevel = stu5.HealthLevel, SchoolClass = stu5.SchoolClass };
});
Console.WriteLine(test1.TotalMilliseconds);

直接最简单New对象赋值的操作耗时是100万次34毫秒,也就是0.034秒的样子差距还是挺大的。

可见这样的性能是注定不可能成为一个好用的组件的!更别谈高性能了。

于是便开始了在茫茫的代码库中淘取黑科技的一波操作。很感谢现代的搜索引擎,让我能在迷失的黑夜中找到一盏明灯。

反射造成的性能问题是我迫切要解决的,反射性能的规避通常有以下几种方式进行提升:

  1. 缓存,高并发场景下绝对是要避免每次都去调用反射代码的,将反射部分尽量缓存下来。
  2. 调用C/C++代码库,但是首先要懂C/C++,其次要保证调用dll的时间要将反射的时间换取回来。
  3. 通过Expression Tree的方式构造表达式树,然后将表达式树缓存起来,这样在之后调用的代码都是等价于直接执行代码的。
  4. 通过IL Emit的方式动态构造代码,更为底层的IL代码提供更高的效率。

在这四种方案中,我聚焦到了最后两种上。

Activator.CreateInstance<TValue>()

因为反射即便再怎么缓存,上述构建新实例的方法也是很耗费性能的(更别说由于临时工赶.net类库的问题,哈哈哈,太搞笑,可以参考此博文跟随老一辈程序员关于该方法性能的探讨以及源码的剖析http://www.cnblogs.com/leven/archive/2009/12/08/instanse_create_comparison.html),其次还有通过属性去获取值。这些都是难以通过缓存解决的。

IL Emit的实现方式是比较复杂的,稍一不慎,还容易造成内存泄漏等严重的问题。让我一个半吊子程序员来写这么细致的代码,暂时选择回避。

我选择了第三种:通过Expression Tree的方式构造表达式树,并且缓存委托方法进行调用的方法。表达式树这里就不进行详细讲解了,会放在其他博文里面单独讲解(也是一大块学问哦)。

和第一版大同小异,也是通过泛型的思想进行构建。不同的是泛型从方法上传递改为了类上传递,这也是由于缓存Func部分的泛型难以直接传递转换的方式,这个对使用并无太大影响。

第二版 Expression Tree 表达式树方式实现代码:

为了代码通用,抽取了公共部分构造了一个内部的类进行对表达式树的构建操作:

 /*********************************************************
* CopyRight: 7TINY CODE BUILDER.
* Version: 5.0.0
* Author: 7tiny
* Address: Earth
* Create: 2018-04-09 16:55:16
* Modify: 2018-04-09 16:55:16
* E-mail: dong@7tiny.com | sevenTiny@foxmail.com
* GitHub: https://github.com/sevenTiny
* Personal web site: http://www.7tiny.com
* Technical WebSit: http://www.cnblogs.com/7tiny/
* Description:
* Thx , Best Regards ~
*********************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection; namespace SevenTiny.Bantina.AutoMapper
{
internal sealed class MapperExpressionCommon
{
/// <summary>
/// structure func
/// </summary>
/// <param name="outType"></param>
/// <param name="inTypes"></param>
/// <param name="memberInitExpression"></param>
/// <param name="parameterExpressionList"></param>
public static void GetFunc(Type outType, Type[] inTypes, out MemberInitExpression memberInitExpression, out List<ParameterExpression> parameterExpressionList)
{
parameterExpressionList = new List<ParameterExpression>();
List<MemberBinding> memberBindingList = new List<MemberBinding>();
PropertyInfo[] propertyInfos = outType.GetProperties();
Dictionary<string, PropertyInfo> outPropertyDic = propertyInfos.ToDictionary(t => t.Name, t => t);
foreach (var inType in inTypes)
{
ParameterExpression parameterExpression = Expression.Parameter(inType, inType.FullName);
PropertyInfo[] inTypePpropertyInfos = inType.GetProperties();
foreach (var inTypeInfo in inTypePpropertyInfos)
{
if (inTypeInfo.GetCustomAttribute(typeof(DoNotMapperAttribute)) == null)
{
//first
string outPropertyDicKey = MapperAttribute.GetTargetName(inTypeInfo);
//second
if (string.IsNullOrEmpty(outPropertyDicKey) && outPropertyDic.Keys.Contains(inTypeInfo.Name))
{
outPropertyDicKey = inTypeInfo.Name;
}
//third
if (!string.IsNullOrEmpty(outPropertyDicKey) && outPropertyDic.Keys.Contains(outPropertyDicKey))
{
MemberExpression property = Expression.Property(parameterExpression, inTypeInfo);
MemberBinding memberBinding = Expression.Bind(outPropertyDic[outPropertyDicKey], property);
memberBindingList.Add(memberBinding);
outPropertyDic.Remove(outPropertyDicKey);//remove property if has be valued
}
}
}
if (!parameterExpressionList.Exists(t => t.Name.Equals(parameterExpression.Name)))
{
parameterExpressionList.Add(parameterExpression);
}
}
memberInitExpression = Expression.MemberInit(Expression.New(outType), memberBindingList.ToArray());
}
} }

在上面的这段代码中:

 ParameterExpression parameterExpression = Expression.Parameter(inType, inType.FullName); 

构建了类似 t 的结构。

MemberExpression property = Expression.Property(parameterExpression, inTypeInfo);
MemberBinding memberBinding = Expression.Bind(outPropertyDic[outPropertyDicKey], property);

构建了类似 HealthLevel = t.HealthLevel 的结构。

memberInitExpression = Expression.MemberInit(Expression.New(outType), memberBindingList.ToArray());

构建了类似 new Student() {HealthLevel = t.HealthLevel, SchoolClass = t.SchoolClass} 的结构。

这段逻辑代码的调用方代码:

 /*********************************************************
* CopyRight: 7TINY CODE BUILDER.
* Version: 5.0.0
* Author: 7tiny
* Address: Earth
* Create: 2018-03-16 10:11:43
* Modify: 2018-4-3 11:35:53
* E-mail: dong@7tiny.com | sevenTiny@foxmail.com
* GitHub: https://github.com/sevenTiny
* Personal web site: http://www.7tiny.com
* Technical WebSit: http://www.cnblogs.com/7tiny/
* Description:
* Thx , Best Regards ~
*********************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection; namespace SevenTiny.Bantina.AutoMapper
{
public sealed class Mapper<TIn, TOut> where TOut : class where TIn : class
{
private Mapper() { }
private static readonly Func<TIn, TOut> funcCache = GetFunc();
public static TOut AutoMapper(TIn tIn)
{
return funcCache(tIn);
}
public static TOut AutoMapper(TIn tIn, Action<TOut> action)
{
TOut outValue = funcCache(tIn);
action(outValue);
return outValue;
}
private static Func<TIn, TOut> GetFunc()
{
Type[] types = new Type[] { typeof(TIn) };
MemberInitExpression memberInitExpression;
List<ParameterExpression> parameterExpressionList;
MapperExpressionCommon.GetFunc(typeof(TOut), types, out memberInitExpression, out parameterExpressionList);
Expression<Func<TIn, TOut>> lambda = Expression.Lambda<Func<TIn, TOut>>(memberInitExpression, parameterExpressionList);
return lambda.Compile();
}
}
}

在上面这段代码中:

Expression<Func<TIn, TOut>> lambda = Expression.Lambda<Func<TIn, TOut>>(memberInitExpression, parameterExpressionList);

使用上面的公共方法out出的参数构建了一个完整的lambda表达式  t => new Student() {HealthLevel = t.HealthLevel, SchoolClass = t.SchoolClass}

从代码断点中也可以看出:

这里的t由于公共方法中使用的是 type(T).FullName,所以看起来比较长,是Test.SevenTiny.Bantina.Model.Student5,可以看成是一个小写的“t”,这里是等效的。

lambda.Compile()

将上述的表达式执行为Func<TIn,TOut> 的一个匿名委托。

private static readonly Func<TIn, TOut> funcCache = GetFunc();

将执行后的结果(匿名委托)缓存起来。

这样最终的执行结果便转换成了一段代码:

Func<Student5,Student> stuFunc = t=>new Student() {HealthLevel = t.HealthLevel, SchoolClass = t.SchoolClass};

调用这段代码和直接实例化方法赋值是等效的,但是调用这段代码可以将很多通用的方法封装成一个公共的方法,提高了代码的重用性。

这段代码耗时的部分便是通过一定的反射构建出一个Func<T,T2>所耗费的时间,这在数百万次的调用中仅仅调用了一次,而执行部分却是不耗费时间的,在高并发的场景下占有很大的优势。

下面我们实际测试一下:

同样的代码,使用了第二版的AutoMapper,执行100万次

 Student5 stu5 = new Student5 { HealthLevel = , SchoolClass = new SchoolClass { Name = "class1" } };

 var test1 = StopwatchHelper.Caculate(, () =>
{
Student stu = Mapper<Student5, Student>.AutoMapper(stu5, t => t.Name = "jony");
});
Console.WriteLine(test1.TotalMilliseconds);

我们加大调用的次数再次进行比较:

 Student5 stu5 = new Student5 { HealthLevel = , SchoolClass = new SchoolClass { Name = "class1" } };

 var test0 = StopwatchHelper.Caculate(, () =>
{
Student stu = Mapper.AutoMapper<Student,Student5>(stu5, t => t.Name = "jony");
});
Console.WriteLine("使用反射调用 1 百万次耗时:");
Console.WriteLine(test0.TotalMilliseconds); Console.WriteLine(); var test1 = StopwatchHelper.Caculate(, () =>
{
Student stu = Mapper<Student5, Student>.AutoMapper(stu5, t => t.Name = "jony");
});
Console.WriteLine("使用Expression表达式树调用 1 百万次耗时:");
Console.WriteLine(test1.TotalMilliseconds); Console.WriteLine(); var test2 = StopwatchHelper.Caculate(, () =>
{
Student stu = new Student { HealthLevel = stu5.HealthLevel, Name = "jony" };
});
Console.WriteLine("使用代码直接构建 1 百万次耗时:");
Console.WriteLine(test2.TotalMilliseconds);

一百万次代码执行,Expression表达式树方式耗时0.084秒,而直接写代码赋值的方式耗时0.022秒,但是使用反射的方式却使用了3.6秒。

执行次数越多,Expression表达式树的性能便越接近直接调用代码的方式。

【总结】

通过本次基础组建的完成以及一次优化,我从中学习到了Expression表达式树的实现方法以及突出的性能优势,虽然相比反射的写法更加复杂一些。

在接下来的优化中,可能会加入对Emit的支持,到时候便是一场Emit和Expression的大战,不过我再次预测结果:应该差距不是很大~ 敬请期待...

本文的完整代码以在我的github中开源,可以直接clone下来查看验证。

https://github.com/sevenTiny/SevenTiny.Bantina

下载下来的项目有多种基础组件,直接查看该组件,当然有对其他组件的意见或建议也可以直接提出来探讨,共同学习哦~

不便于github查看的同学,我这里提供了完整的代码(是有多个对象映射的重载方法的完整版本)。

公共Attribute特性标签部分:

 /*********************************************************
* CopyRight: 7TINY CODE BUILDER.
* Version: 5.0.0
* Author: 7tiny
* Address: Earth
* Create: 2018-04-09 16:55:16
* Modify: 2018-04-09 16:55:16
* E-mail: dong@7tiny.com | sevenTiny@foxmail.com
* GitHub: https://github.com/sevenTiny
* Personal web site: http://www.7tiny.com
* Technical WebSit: http://www.cnblogs.com/7tiny/
* Description:
* Thx , Best Regards ~
*********************************************************/
using System; namespace SevenTiny.Bantina.AutoMapper
{
[AttributeUsage(AttributeTargets.Property, Inherited = true)]
public class DoNotMapperAttribute :Attribute
{
}
}
 /*********************************************************
* CopyRight: 7TINY CODE BUILDER.
* Version: 5.0.0
* Author: 7tiny
* Address: Earth
* Create: 2018-04-03 13:28:38
* Modify: 2018-04-03 13:28:38
* E-mail: dong@7tiny.com | sevenTiny@foxmail.com
* GitHub: https://github.com/sevenTiny
* Personal web site: http://www.7tiny.com
* Technical WebSit: http://www.cnblogs.com/7tiny/
* Description:
* Thx , Best Regards ~
*********************************************************/
using System;
using System.Linq;
using System.Reflection; namespace SevenTiny.Bantina.AutoMapper
{
[AttributeUsage(AttributeTargets.Property, Inherited = true)]
public class MapperAttribute : Attribute
{
public string TargetName { get; set; } public MapperAttribute() { }
public MapperAttribute(string targetName)
{
this.TargetName = targetName;
} public static string GetTargetName(PropertyInfo property)
{
var attr = property.GetCustomAttributes<MapperAttribute>(true).FirstOrDefault();
return attr != null ? (attr as MapperAttribute).TargetName ?? default(string) : default(string);
}
}
}
 /*********************************************************
* CopyRight: 7TINY CODE BUILDER.
* Version: 5.0.0
* Author: 7tiny
* Address: Earth
* Create: 2018-04-03 13:28:38
* Modify: 2018-04-03 13:28:38
* E-mail: dong@7tiny.com | sevenTiny@foxmail.com
* GitHub: https://github.com/sevenTiny
* Personal web site: http://www.7tiny.com
* Technical WebSit: http://www.cnblogs.com/7tiny/
* Description:
* Thx , Best Regards ~
*********************************************************/
using System;
using System.Linq; namespace SevenTiny.Bantina.AutoMapper
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Field, Inherited = true)]
public class MapperClassAttribute : Attribute
{
public string Name { get; set; }
public static string GetName(Type type)
{
var attr = type.GetCustomAttributes(typeof(MapperClassAttribute), true).FirstOrDefault();
return attr != null ? (attr as MapperClassAttribute).Name ?? default(string) : default(string);
}
}
}

一、反射版本

 /*********************************************************
* CopyRight: 7TINY CODE BUILDER.
* Version: 5.0.0
* Author: 7tiny
* Address: Earth
* Create: 2018-03-16 10:11:43
* Modify: 2018-4-3 11:35:53
* E-mail: dong@7tiny.com | sevenTiny@foxmail.com
* GitHub: https://github.com/sevenTiny
* Personal web site: http://www.7tiny.com
* Technical WebSit: http://www.cnblogs.com/7tiny/
* Description:
* Thx , Best Regards ~
*********************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection; namespace SevenTiny.Bantina.AutoMapper
{
public sealed class Mapper
{
private Mapper() { }
/// <summary>
/// Init Source Value Dic
/// </summary>
/// <typeparam name="TSource"></typeparam>
/// <param name="source"></param>
/// <returns></returns>
private static Dictionary<string, object> Initdic<TSource>(TSource source)
{
Dictionary<string, object> dic = new Dictionary<string, object>();
foreach (PropertyInfo property in typeof(TSource).GetProperties())
{
string targetPropertyName = MapperAttribute.GetTargetName(property);
if (!string.IsNullOrEmpty(targetPropertyName))
{
if (!dic.ContainsKey(targetPropertyName))
{
dic.Add(targetPropertyName, property.GetValue(source));
}
}
else if (!dic.ContainsKey(property.Name))
{
dic.Add(property.Name, property.GetValue(source));
}
}
return dic;
}
/// <summary>
/// SetValue from propertyinfo and sourceDictionary
/// </summary>
/// <typeparam name="TValue"></typeparam>
/// <param name="value"></param>
/// <param name="propertyInfos"></param>
/// <param name="sourceDic"></param>
/// <returns></returns>
private static TValue SetValue<TValue>(TValue value, PropertyInfo[] propertyInfos, Dictionary<string, object> sourceDic, Dictionary<string, string> keys) where TValue : class
{
foreach (PropertyInfo property in propertyInfos)
{
if (!keys.ContainsKey(property.Name))
{
if (sourceDic.ContainsKey(property.Name))
{
try
{
property.SetValue(value, sourceDic[property.Name]);
keys.Add(property.Name, string.Empty);
}
catch (Exception)
{
property.SetValue(value, null);
}
}
}
}
return value;
}
/// <summary>
/// AutoMapper
/// </summary>
/// <typeparam name="TValue">value type</typeparam>
/// <typeparam name="TSource">source type</typeparam>
/// <param name="source"></param>
/// <returns></returns>
public static TValue AutoMapper<TValue, TSource>(TSource source) where TValue : class where TSource : class
{
TValue value = Activator.CreateInstance<TValue>();
PropertyInfo[] propertyInfos = typeof(TValue).GetProperties();
Dictionary<string, string> keys = new Dictionary<string, string>();
value = SetValue(value, propertyInfos, Initdic(source), keys);
return value;
}
/// <summary>
/// AutoMapper,Support for Use Action to custom special fields.
/// </summary>
/// <typeparam name="TValue"></typeparam>
/// <typeparam name="TSource"></typeparam>
/// <param name="source"></param>
/// <param name="action"></param>
/// <returns></returns>
public static TValue AutoMapper<TValue, TSource>(TSource source, Action<TValue> action) where TValue : class where TSource : class
{
TValue value = Activator.CreateInstance<TValue>();
PropertyInfo[] propertyInfos = typeof(TValue).GetProperties();
Dictionary<string, string> keys = new Dictionary<string, string>();
value = SetValue(value, propertyInfos, Initdic(source), keys);
action(value);
return value;
}
/// <summary>
/// AutoMapper with multitype properties.
/// </summary>
/// <typeparam name="TValue"></typeparam>
/// <typeparam name="TSource1"></typeparam>
/// <typeparam name="TSource2"></typeparam>
/// <param name="source1"></param>
/// <param name="source2"></param>
/// <returns></returns>
public static TValue AutoMapper<TValue, TSource1, TSource2>(TSource1 source1, TSource2 source2) where TValue : class where TSource1 : class where TSource2 : class
{
TValue value = Activator.CreateInstance<TValue>();
PropertyInfo[] propertyInfos = typeof(TValue).GetProperties();
Dictionary<string, string> keys = new Dictionary<string, string>();
value = SetValue(value, propertyInfos, Initdic(source1), keys);
value = SetValue(value, propertyInfos, Initdic(source2), keys);
return value;
}
/// <summary>
/// AutoMapper with multitype properties.Support for Use Action to custom special fields.
/// </summary>
/// <typeparam name="TValue"></typeparam>
/// <typeparam name="TSource1"></typeparam>
/// <typeparam name="TSource2"></typeparam>
/// <param name="source1"></param>
/// <param name="source2"></param>
/// <param name="action"></param>
/// <returns></returns>
public static TValue AutoMapper<TValue, TSource1, TSource2>(TSource1 source1, TSource2 source2, Action<TValue> action) where TValue : class where TSource1 : class where TSource2 : class
{
TValue value = Activator.CreateInstance<TValue>();
PropertyInfo[] propertyInfos = typeof(TValue).GetProperties();
Dictionary<string, string> keys = new Dictionary<string, string>();
value = SetValue(value, propertyInfos, Initdic(source1), keys);
value = SetValue(value, propertyInfos, Initdic(source2), keys);
action(value);
return value;
}
/// <summary>
/// AutoMapper with multitype properties.
/// </summary>
/// <typeparam name="TValue"></typeparam>
/// <typeparam name="TSource1"></typeparam>
/// <typeparam name="TSource2"></typeparam>
/// <typeparam name="TSource3"></typeparam>
/// <param name="source1"></param>
/// <param name="source2"></param>
/// <param name="source3"></param>
/// <returns></returns>
public static TValue AutoMapper<TValue, TSource1, TSource2, TSource3>(TSource1 source1, TSource2 source2, TSource3 source3) where TValue : class where TSource1 : class where TSource2 : class where TSource3 : class
{
TValue value = Activator.CreateInstance<TValue>();
PropertyInfo[] propertyInfos = typeof(TValue).GetProperties();
Dictionary<string, string> keys = new Dictionary<string, string>();
value = SetValue(value, propertyInfos, Initdic(source1), keys);
value = SetValue(value, propertyInfos, Initdic(source2), keys);
value = SetValue(value, propertyInfos, Initdic(source3), keys);
return value;
}
/// <summary>
/// AutoMapper with multitype properties.Support for Use Action to custom special fields.
/// </summary>
/// <typeparam name="TValue"></typeparam>
/// <typeparam name="TSource1"></typeparam>
/// <typeparam name="TSource2"></typeparam>
/// <typeparam name="TSource3"></typeparam>
/// <param name="source1"></param>
/// <param name="source2"></param>
/// <param name="source3"></param>
/// <param name="action"></param>
/// <returns></returns>
public static TValue AutoMapper<TValue, TSource1, TSource2, TSource3>(TSource1 source1, TSource2 source2, TSource3 source3, Action<TValue> action) where TValue : class where TSource1 : class where TSource2 : class where TSource3 : class
{
TValue value = Activator.CreateInstance<TValue>();
PropertyInfo[] propertyInfos = typeof(TValue).GetProperties();
Dictionary<string, string> keys = new Dictionary<string, string>();
value = SetValue(value, propertyInfos, Initdic(source1), keys);
value = SetValue(value, propertyInfos, Initdic(source2), keys);
value = SetValue(value, propertyInfos, Initdic(source3), keys);
action(value);
return value;
}
/// <summary>
/// AutoMapper with multitype properties.
/// </summary>
/// <typeparam name="TValue"></typeparam>
/// <typeparam name="TSource1"></typeparam>
/// <typeparam name="TSource2"></typeparam>
/// <typeparam name="TSource3"></typeparam>
/// <typeparam name="TSource4"></typeparam>
/// <param name="source1"></param>
/// <param name="source2"></param>
/// <param name="source3"></param>
/// <param name="source4"></param>
/// <returns></returns>
public static TValue AutoMapper<TValue, TSource1, TSource2, TSource3, TSource4>(TSource1 source1, TSource2 source2, TSource3 source3, TSource4 source4) where TValue : class where TSource1 : class where TSource2 : class where TSource3 : class where TSource4 : class
{
TValue value = Activator.CreateInstance<TValue>();
PropertyInfo[] propertyInfos = typeof(TValue).GetProperties();
Dictionary<string, string> keys = new Dictionary<string, string>();
value = SetValue(value, propertyInfos, Initdic(source1), keys);
value = SetValue(value, propertyInfos, Initdic(source2), keys);
value = SetValue(value, propertyInfos, Initdic(source3), keys);
value = SetValue(value, propertyInfos, Initdic(source4), keys);
return value;
}
/// <summary>
/// AutoMapper with multitype properties.Support for Use Action to custom special fields.
/// </summary>
/// <typeparam name="TValue"></typeparam>
/// <typeparam name="TSource1"></typeparam>
/// <typeparam name="TSource2"></typeparam>
/// <typeparam name="TSource3"></typeparam>
/// <typeparam name="TSource4"></typeparam>
/// <param name="source1"></param>
/// <param name="source2"></param>
/// <param name="source3"></param>
/// <param name="source4"></param>
/// <param name="action"></param>
/// <returns></returns>
public static TValue AutoMapper<TValue, TSource1, TSource2, TSource3, TSource4>(TSource1 source1, TSource2 source2, TSource3 source3, TSource4 source4, Action<TValue> action) where TValue : class where TSource1 : class where TSource2 : class where TSource3 : class where TSource4 : class
{
TValue value = Activator.CreateInstance<TValue>();
PropertyInfo[] propertyInfos = typeof(TValue).GetProperties();
Dictionary<string, string> keys = new Dictionary<string, string>();
value = SetValue(value, propertyInfos, Initdic(source1), keys);
value = SetValue(value, propertyInfos, Initdic(source2), keys);
value = SetValue(value, propertyInfos, Initdic(source3), keys);
value = SetValue(value, propertyInfos, Initdic(source4), keys);
action(value);
return value;
}
/// <summary>
/// AutoMapper with multitype properties.
/// </summary>
/// <typeparam name="TValue"></typeparam>
/// <typeparam name="TSource1"></typeparam>
/// <typeparam name="TSource2"></typeparam>
/// <typeparam name="TSource3"></typeparam>
/// <typeparam name="TSource4"></typeparam>
/// <typeparam name="TSource5"></typeparam>
/// <param name="source1"></param>
/// <param name="source2"></param>
/// <param name="source3"></param>
/// <param name="source4"></param>
/// <param name="source5"></param>
/// <returns></returns>
public static TValue AutoMapper<TValue, TSource1, TSource2, TSource3, TSource4, TSource5>(TSource1 source1, TSource2 source2, TSource3 source3, TSource4 source4, TSource5 source5) where TValue : class where TSource1 : class where TSource2 : class where TSource3 : class where TSource4 : class where TSource5 : class
{
TValue value = Activator.CreateInstance<TValue>();
PropertyInfo[] propertyInfos = typeof(TValue).GetProperties();
Dictionary<string, string> keys = new Dictionary<string, string>();
value = SetValue(value, propertyInfos, Initdic(source1), keys);
value = SetValue(value, propertyInfos, Initdic(source2), keys);
value = SetValue(value, propertyInfos, Initdic(source3), keys);
value = SetValue(value, propertyInfos, Initdic(source4), keys);
value = SetValue(value, propertyInfos, Initdic(source5), keys);
return value;
}
/// <summary>
/// AutoMapper with multitype properties.Support for Use Action to custom special fields.
/// </summary>
/// <typeparam name="TValue"></typeparam>
/// <typeparam name="TSource1"></typeparam>
/// <typeparam name="TSource2"></typeparam>
/// <typeparam name="TSource3"></typeparam>
/// <typeparam name="TSource4"></typeparam>
/// <typeparam name="TSource5"></typeparam>
/// <param name="source1"></param>
/// <param name="source2"></param>
/// <param name="source3"></param>
/// <param name="source4"></param>
/// <param name="source5"></param>
/// <param name="action"></param>
/// <returns></returns>
public static TValue AutoMapper<TValue, TSource1, TSource2, TSource3, TSource4, TSource5>(TSource1 source1, TSource2 source2, TSource3 source3, TSource4 source4, TSource5 source5, Action<TValue> action) where TValue : class where TSource1 : class where TSource2 : class where TSource3 : class where TSource4 : class where TSource5 : class
{
TValue value = Activator.CreateInstance<TValue>();
PropertyInfo[] propertyInfos = typeof(TValue).GetProperties();
Dictionary<string, string> keys = new Dictionary<string, string>();
value = SetValue(value, propertyInfos, Initdic(source1), keys);
value = SetValue(value, propertyInfos, Initdic(source2), keys);
value = SetValue(value, propertyInfos, Initdic(source3), keys);
value = SetValue(value, propertyInfos, Initdic(source4), keys);
value = SetValue(value, propertyInfos, Initdic(source5), keys);
action(value);
return value;
}
}
}

二、Expression Tree 表达式树版本

 /*********************************************************
* CopyRight: 7TINY CODE BUILDER.
* Version: 5.0.0
* Author: 7tiny
* Address: Earth
* Create: 2018-03-16 10:11:43
* Modify: 2018-4-3 11:35:53
* E-mail: dong@7tiny.com | sevenTiny@foxmail.com
* GitHub: https://github.com/sevenTiny
* Personal web site: http://www.7tiny.com
* Technical WebSit: http://www.cnblogs.com/7tiny/
* Description:
* Thx , Best Regards ~
*********************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection; namespace SevenTiny.Bantina.AutoMapper
{
public sealed class Mapper<TIn, TOut> where TOut : class where TIn : class
{
private Mapper() { }
private static readonly Func<TIn, TOut> funcCache = GetFunc();
public static TOut AutoMapper(TIn tIn)
{
return funcCache(tIn);
}
public static TOut AutoMapper(TIn tIn, Action<TOut> action)
{
TOut outValue = funcCache(tIn);
action(outValue);
return outValue;
}
private static Func<TIn, TOut> GetFunc()
{
Type[] types = new Type[] { typeof(TIn) };
MemberInitExpression memberInitExpression;
List<ParameterExpression> parameterExpressionList;
MapperExpressionCommon.GetFunc(typeof(TOut), types, out memberInitExpression, out parameterExpressionList);
Expression<Func<TIn, TOut>> lambda = Expression.Lambda<Func<TIn, TOut>>(memberInitExpression, parameterExpressionList);
return lambda.Compile();
}
}
public sealed class Mapper<TIn1, TIn2, TOut> where TOut : class where TIn1 : class where TIn2 : class
{
private Mapper() { }
public static TOut AutoMapper(TIn1 tIn1, TIn2 tIn2)
{
return funcCache(tIn1, tIn2);
}
public static TOut AutoMapper(TIn1 tIn1, TIn2 tIn2, Action<TOut> action)
{
TOut outValue = funcCache(tIn1, tIn2);
action(outValue);
return outValue;
}
private static readonly Func<TIn1, TIn2, TOut> funcCache = GetFunc();
private static Func<TIn1, TIn2, TOut> GetFunc()
{
Type[] types = new Type[] { typeof(TIn1), typeof(TIn2) };
MemberInitExpression memberInitExpression;
List<ParameterExpression> parameterExpressionList;
MapperExpressionCommon.GetFunc(typeof(TOut), types, out memberInitExpression, out parameterExpressionList);
Expression<Func<TIn1, TIn2, TOut>> lambda = Expression.Lambda<Func<TIn1, TIn2, TOut>>(memberInitExpression, parameterExpressionList);
return lambda.Compile();
}
}
public sealed class Mapper<TIn1, TIn2, TIn3, TOut> where TOut : class where TIn1 : class where TIn2 : class where TIn3 : class
{
private Mapper() { }
public static TOut AutoMapper(TIn1 tIn1, TIn2 tIn2, TIn3 tIn3)
{
return funcCache(tIn1, tIn2, tIn3);
}
public static TOut AutoMapper(TIn1 tIn1, TIn2 tIn2, TIn3 tIn3, Action<TOut> action)
{
TOut outValue = funcCache(tIn1, tIn2, tIn3);
action(outValue);
return outValue;
}
private static readonly Func<TIn1, TIn2, TIn3, TOut> funcCache = GetFunc();
private static Func<TIn1, TIn2, TIn3, TOut> GetFunc()
{
Type[] types = new Type[] { typeof(TIn1), typeof(TIn2), typeof(TIn3) };
MemberInitExpression memberInitExpression;
List<ParameterExpression> parameterExpressionList;
MapperExpressionCommon.GetFunc(typeof(TOut), types, out memberInitExpression, out parameterExpressionList);
Expression<Func<TIn1, TIn2, TIn3, TOut>> lambda = Expression.Lambda<Func<TIn1, TIn2, TIn3, TOut>>(memberInitExpression, parameterExpressionList);
return lambda.Compile();
}
}
public sealed class Mapper<TIn1, TIn2, TIn3, TIn4, TOut> where TOut : class where TIn1 : class where TIn2 : class where TIn3 : class where TIn4 : class
{
private Mapper() { }
public static TOut AutoMapper(TIn1 tIn1, TIn2 tIn2, TIn3 tIn3, TIn4 tIn4)
{
return funcCache(tIn1, tIn2, tIn3, tIn4);
}
public static TOut AutoMapper(TIn1 tIn1, TIn2 tIn2, TIn3 tIn3, TIn4 tIn4, Action<TOut> action)
{
TOut outValue = funcCache(tIn1, tIn2, tIn3, tIn4);
action(outValue);
return outValue;
}
private static readonly Func<TIn1, TIn2, TIn3, TIn4, TOut> funcCache = GetFunc();
private static Func<TIn1, TIn2, TIn3, TIn4, TOut> GetFunc()
{
Type[] types = new Type[] { typeof(TIn1), typeof(TIn2), typeof(TIn3), typeof(TIn4) };
MemberInitExpression memberInitExpression;
List<ParameterExpression> parameterExpressionList;
MapperExpressionCommon.GetFunc(typeof(TOut), types, out memberInitExpression, out parameterExpressionList);
Expression<Func<TIn1, TIn2, TIn3, TIn4, TOut>> lambda = Expression.Lambda<Func<TIn1, TIn2, TIn3, TIn4, TOut>>(memberInitExpression, parameterExpressionList);
return lambda.Compile();
}
}
public sealed class Mapper<TIn1, TIn2, TIn3, TIn4, TIn5, TOut> where TOut : class where TIn1 : class where TIn2 : class where TIn3 : class where TIn4 : class where TIn5 : class
{
private Mapper() { }
public static TOut AutoMapper(TIn1 tIn1, TIn2 tIn2, TIn3 tIn3, TIn4 tIn4, TIn5 tIn5)
{
return funcCache(tIn1, tIn2, tIn3, tIn4, tIn5);
}
public static TOut AutoMapper(TIn1 tIn1, TIn2 tIn2, TIn3 tIn3, TIn4 tIn4, TIn5 tIn5, Action<TOut> action)
{
TOut outValue = funcCache(tIn1, tIn2, tIn3, tIn4, tIn5);
action(outValue);
return outValue;
}
private static readonly Func<TIn1, TIn2, TIn3, TIn4, TIn5, TOut> funcCache = GetFunc();
private static Func<TIn1, TIn2, TIn3, TIn4, TIn5, TOut> GetFunc()
{
Type[] types = new Type[] { typeof(TIn1), typeof(TIn2), typeof(TIn3), typeof(TIn4), typeof(TIn5) };
MemberInitExpression memberInitExpression;
List<ParameterExpression> parameterExpressionList;
MapperExpressionCommon.GetFunc(typeof(TOut), types, out memberInitExpression, out parameterExpressionList);
Expression<Func<TIn1, TIn2, TIn3, TIn4, TIn5, TOut>> lambda = Expression.Lambda<Func<TIn1, TIn2, TIn3, TIn4, TIn5, TOut>>(memberInitExpression, parameterExpressionList);
return lambda.Compile();
}
}
}

自己造轮子系列之OOM框架AutoMapper的更多相关文章

  1. 造轮子系列之RPC 1:如何从零开始开发RPC框架

    前言 RPC 框架是后端攻城狮永远都绕不开的知识点,目前业界比较知名有 Dubbo.Spring Cloud 等.很多人都停留在了只会用的阶段,作为程序猿,拥有好奇心深入学习,才能有效提高自己的竞争力 ...

  2. 重复造轮子系列——基于Ocelot实现类似支付宝接口模式的网关

    重复造轮子系列——基于Ocelot实现类似支付宝接口模式的网关 引言 重复造轮子系列是自己平时的一些总结.有的轮子依赖社区提供的轮子为基础,这里把使用过程的一些觉得有意思的做个分享.有些思路或者方法在 ...

  3. 重复造轮子系列——基于FastReport设计打印模板实现桌面端WPF套打和商超POS高度自适应小票打印

    重复造轮子系列——基于FastReport设计打印模板实现桌面端WPF套打和商超POS高度自适应小票打印 一.引言 桌面端系统经常需要对接各种硬件设备,比如扫描器.读卡器.打印机等. 这里介绍下桌面端 ...

  4. 避免重复造轮子的UI自动化测试框架开发

    一懒起来就好久没更新文章了,其实懒也还是因为忙,今年上半年的加班赶上了去年一年的加班,加班不息啊,好了吐槽完就写写一直打算继续的自动化开发 目前各种UI测试框架层出不穷,但是万变不离其宗,驱动PC浏览 ...

  5. 五步掌握OOM框架AutoMapper基本使用

    本文版权归博客园和作者吴双本人共同所有,转载和爬虫请注明原文地址 www.cnblogs.com/tdws  写在前面 OOM顾名思义,Object-Object-Mapping实体间相互转换,Aut ...

  6. OOM框架AutoMapper基本使用(2)

    出于安全考虑,在后台与前台进行数据传输时,往往不会直接传输实体模型,而是使用Dto(Data transfer object 数据传输对象),这样在后台往前台传递数据时可以省略不必要的信息,只保留必要 ...

  7. OOM框架AutoMapper基本使用(1)

    OOM顾名思义,Object-Object-Mapping实体间相互转换,AutoMapper也是个老生常谈了,其意义在于帮助你无需手动的转换简单而又麻烦的实体间关系,比如ViewModel和enti ...

  8. 造轮子系列(三): 一个简单快速的html虚拟语法树(AST)解析器

    前言 虚拟语法树(Abstract Syntax Tree, AST)是解释器/编译器进行语法分析的基础, 也是众多前端编译工具的基础工具, 比如webpack, postcss, less等. 对于 ...

  9. 重复造轮子系列--dijkstra算法

    前年一时脑热(理想很丰满,现实很骨感),写了这个最短路径优先的低效版本,且留着回忆吧. spf.h #ifndef SPF_H_ #define SPF_H_ typedef struct { int ...

随机推荐

  1. 所使用的“EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089”版本高于所引用的程序集“EntityFramework, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089”的版本

    错误信息:所使用的"EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089&qu ...

  2. 配置puppet的主机端和客户端的自动认证

    配置puppet的主机端和客户端的自动认证 author:headsen  chen    2017-11-01  17:44:56 个人原创,转载请注明作者,出处,否则依法追究法律责任 1,先在主机 ...

  3. /VAR/LOG/各个日志文件分析

     /VAR/LOG/各个日志文件分析 author:headsen  chen    2017-10-24   18:00:24 部分内容取自网上搜索,部分内容为自己整理的,特此声明. 1.   /v ...

  4. shell脚本中文件测试

    shell脚本中文件测试 author:headsen chen  2017-10-17  14:35:19 个人原创,转载请注明作者,否则 依法追究法律责任 [ -f  filename  ]   ...

  5. [Android]利用run-as命令在不root情况下读取data下面的数据

    正文 一.关键步骤 主要是run-as命令: over@over-ThinkPad-R52:~$ adb shell  $ run-as com.package  $ cd /data/data/co ...

  6. .Net开发之旅(一个年少轻狂的程序员的感慨)

    高端大气上档次.这次当时一个身为懵懂初中生的我对程序员这一职位的描述.那时虽不是随处都能看到黑客大军的波及,但至少是知道所谓的黑客爸爸的厉害,一言不合说被黑就被黑.对于懵懂的我那是一种向往.自己也曾想 ...

  7. 四则运算程序(java基于控制台)

    四则运算题目生成程序(基于控制台) 一.题目描述: 1. 使用 -n 参数控制生成题目的个数,例如 Myapp.exe -n 10 -o Exercise.txt 将生成10个题目. 2. 使用 -r ...

  8. linux分析、诊断及调优必备的“杀器”之二

    先说明下,之所以同类内容分成多篇文章,不是为了凑篇数,而是为了便于自己和大家阅读,下面继续: 7.sar The sar command is used to collect, report, and ...

  9. 上海依图-电话面试-angularjs

    树的遍历(树结构:node.name,node.children),输出node.name(递归) 指令的scope的绑定策略(@绑定DOM数学单向绑定:=双向数据绑定:&绑定父作用域函数) ...

  10. Beta No.5

    今天遇到的困难: 前端大部分代码由我们放逐的组员完成,这影响到了我们解决"Fragment碎片刷新时总产生的固定位置"的进程,很难找到源码对应 新加入的成员对界面代码不熟悉. 我们 ...