OOM框架想必你們在Web開發中是使用頻率很是之高的,若是還不甚瞭解OOM框架,那麼咱們對OOM框架稍做講解。html
OOM顧名思義,Object-Object-Mapping實體間相互轉換。常見的使用場景有兩個實體要經過DTO對象進行頁面的渲染,那麼咱們就須要經過對DTO對象的一個一個屬性進行賦值,最終返回。整個過程是單調又繁瑣的,甚至嚴重影響了代碼的整潔性。更有強迫症高度患者可能看着這一坨shi同樣的代碼陣陣痙攣...所以,咱們就想,能不能用一種簡介的方法進行自動映射兩個,三個甚至更多的對象,避免寫這麼一些低級乏味的代碼。git
因而乎,一大波一大波的自動映射框架悠然而生。業內比較出名的有:AutoMapper,EmitMapper,NLiteMapper,TinyMapper等。在這裏,先拋去他們不談,畢竟未必我的以爲好用,或者我就喜歡造輪子。本篇將講解一波本身造的輪子OOM框架AutoMapper,而且有造輪子過程當中的持續優化,我想,這即是學習的最好過程吧。程序員
本文講解了反射方式的實現方法以及後續拋棄反射的優化方法Expression Tree 表達式樹的實現方法。github
AutoMapper既然是OOM框架,那麼就離不開Object,Object必然又涉及到類型。那麼,泛型是必不可少的。web
框架要簡潔易用,而且能兼容多數場景。因而,咱們準備從如下幾方面進行設計:小程序
項目結構:緩存
首先構造了幾個特性標籤類,用於標註是否要映射,以及映射的自定義別名的配置。併發
DoNotMapperAttribute:不進行映射該屬性app
MapperAttribute:添加自定義名稱的映射配置框架
MapperClassAttribute:該類支持映射(目前沒有實際應用)
1 using System; 2 3 namespace SevenTiny.Bantina.AutoMapper 4 { 5 [AttributeUsage(AttributeTargets.Property, Inherited = true)] 6 public class DoNotMapperAttribute :Attribute 7 { 8 } 9 }
1 using System; 2 using System.Linq; 3 using System.Reflection; 4 5 namespace SevenTiny.Bantina.AutoMapper 6 { 7 [AttributeUsage(AttributeTargets.Property, Inherited = true)] 8 public class MapperAttribute : Attribute 9 { 10 public string TargetName { get; set; } 11 12 public MapperAttribute() { } 13 public MapperAttribute(string targetName) 14 { 15 this.TargetName = targetName; 16 } 17 18 public static string GetTargetName(PropertyInfo property) 19 { 20 var attr = property.GetCustomAttributes<MapperAttribute>(true).FirstOrDefault(); 21 return attr != null ? (attr as MapperAttribute).TargetName ?? default(string) : default(string); 22 } 23 } 24 }
1 using System; 2 using System.Linq; 3 4 namespace SevenTiny.Bantina.AutoMapper 5 { 6 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Field, Inherited = true)] 7 public class MapperClassAttribute : Attribute 8 { 9 public string Name { get; set; } 10 public static string GetName(Type type) 11 { 12 var attr = type.GetCustomAttributes(typeof(MapperClassAttribute), true).FirstOrDefault(); 13 return attr != null ? (attr as MapperClassAttribute).Name ?? default(string) : default(string); 14 } 15 } 16 }
1 public sealed class Mapper 2 { 3 private Mapper() { } 4 /// <summary> 5 /// Init Source Value Dic 6 /// </summary> 7 /// <typeparam name="TSource"></typeparam> 8 /// <param name="source"></param> 9 /// <returns></returns> 10 private static Dictionary<string, object> Initdic<TSource>(TSource source) 11 { 12 Dictionary<string, object> dic = new Dictionary<string, object>(); 13 foreach (PropertyInfo property in typeof(TSource).GetProperties()) 14 { 15 string targetPropertyName = MapperAttribute.GetTargetName(property); 16 if (!string.IsNullOrEmpty(targetPropertyName)) 17 { 18 if (!dic.ContainsKey(targetPropertyName)) 19 { 20 dic.Add(targetPropertyName, property.GetValue(source)); 21 } 22 } 23 else if (!dic.ContainsKey(property.Name)) 24 { 25 dic.Add(property.Name, property.GetValue(source)); 26 } 27 } 28 return dic; 29 } 30 /// <summary> 31 /// SetValue from propertyinfo and sourceDictionary 32 /// </summary> 33 /// <typeparam name="TValue"></typeparam> 34 /// <param name="value"></param> 35 /// <param name="propertyInfos"></param> 36 /// <param name="sourceDic"></param> 37 /// <returns></returns> 38 private static TValue SetValue<TValue>(TValue value, PropertyInfo[] propertyInfos, Dictionary<string, object> sourceDic, Dictionary<string, string> keys) where TValue : class 39 { 40 foreach (PropertyInfo property in propertyInfos) 41 { 42 if (!keys.ContainsKey(property.Name)) 43 { 44 if (sourceDic.ContainsKey(property.Name)) 45 { 46 try 47 { 48 property.SetValue(value, sourceDic[property.Name]); 49 keys.Add(property.Name, string.Empty); 50 } 51 catch (Exception) 52 { 53 property.SetValue(value, null); 54 } 55 } 56 } 57 } 58 return value; 59 } 60 /// <summary> 61 /// AutoMapper 62 /// </summary> 63 /// <typeparam name="TValue">value type</typeparam> 64 /// <typeparam name="TSource">source type</typeparam> 65 /// <param name="source"></param> 66 /// <returns></returns> 67 public static TValue AutoMapper<TValue, TSource>(TSource source) where TValue : class where TSource : class 68 { 69 TValue value = Activator.CreateInstance<TValue>(); 70 PropertyInfo[] propertyInfos = typeof(TValue).GetProperties(); 71 Dictionary<string, string> keys = new Dictionary<string, string>(); 72 value = SetValue(value, propertyInfos, Initdic(source), keys); 73 return value; 74 } 75 /// <summary> 76 /// AutoMapper,Support for Use Action to custom special fields. 77 /// </summary> 78 /// <typeparam name="TValue"></typeparam> 79 /// <typeparam name="TSource"></typeparam> 80 /// <param name="source"></param> 81 /// <param name="action"></param> 82 /// <returns></returns> 83 public static TValue AutoMapper<TValue, TSource>(TSource source, Action<TValue> action) where TValue : class where TSource : class 84 { 85 TValue value = Activator.CreateInstance<TValue>(); 86 PropertyInfo[] propertyInfos = typeof(TValue).GetProperties(); 87 Dictionary<string, string> keys = new Dictionary<string, string>(); 88 value = SetValue(value, propertyInfos, Initdic(source), keys); 89 action(value); 90 return value; 91 } 92 }
因爲思路清晰,實現起來也是很容易的。
經過傳入兩個泛型(一個輸入,一個輸出),而後獲取到其中一個的屬性,並遍歷另外一個的屬性,若是相同,則直接賦值(固然有特性標籤部分的細節處理)。
因爲有些屬性已經賦值過了,再次遇到會從新掃描賦值,下降了性能和影響了邏輯性,這裏作了Dictionary的暫時緩存,以便掃描過的值再也不進行賦值。
特殊的值,咱們使用了Action<T> action 匿名委託的方式進行賦值,提供了特殊處理的能力。
咱們下面進行一波使用,簡單封裝一個屢次調用的測試方法:
1 public static TimeSpan Caculate(int executTimes, Action action) 2 { 3 Stopwatch sw = new Stopwatch(); 4 sw.Start(); 5 for (int i = 0; i < executTimes; i++) 6 { 7 action(); 8 } 9 sw.Stop(); 10 return sw.Elapsed; 11 }
簡單寫一個控制檯應用程序,並傳入參數100萬次調用,並打印執行用時:
1 Student1 stu1 = new Student1 { Uid = Guid.NewGuid() }; 2 Student5 stu5 = new Student5 { HealthLevel = 100, SchoolClass = new SchoolClass { Name = "class1" } }; 3 4 var test1 = StopwatchHelper.Caculate(1000000, () => 5 { 6 Student stu = Mapper.AutoMapper<Student, Student5>(stu5, t => t.Name = "jony"); 7 }); 8 Console.WriteLine(test1.TotalMilliseconds);
再看一眼直接調用的性能:
1 Student5 stu5 = new Student5 { HealthLevel = 100, SchoolClass = new SchoolClass { Name = "class1" } }; 2 3 var test1 = StopwatchHelper.Caculate(1000000, () => 4 { 5 Student stu = new Student { HealthLevel = stu5.HealthLevel, SchoolClass = stu5.SchoolClass }; 6 }); 7 Console.WriteLine(test1.TotalMilliseconds);
可見這樣的性能是註定不可能成爲一個好用的組件的!更別談高性能了。
因而便開始了在茫茫的代碼庫中淘取黑科技的一波操做。很感謝現代的搜索引擎,讓我能在迷失的黑夜中找到一盞明燈。
在這四種方案中,我聚焦到了最後兩種上。
Activator.CreateInstance<TValue>()
由於反射即使再怎麼緩存,上述構建新實例的方法也是很耗費性能的(更別說因爲臨時工趕.net類庫的問題,哈哈哈,太搞笑,能夠參考此博文跟隨老一輩程序員關於該方法性能的探討以及源碼的剖析http://www.cnblogs.com/leven/archive/2009/12/08/instanse_create_comparison.html),其次還有經過屬性去獲取值。這些都是難以經過緩存解決的。
IL Emit的實現方式是比較複雜的,稍一不慎,還容易形成內存泄漏等嚴重的問題。讓我一個半吊子程序員來寫這麼細緻的代碼,暫時選擇迴避。
我選擇了第三種:經過Expression Tree的方式構造表達式樹,而且緩存委託方法進行調用的方法。表達式樹這裏就不進行詳細講解了,會放在其餘博文裏面單獨講解(也是一大塊學問哦)。
和初版大同小異,也是經過泛型的思想進行構建。不一樣的是泛型從方法上傳遞改成了類上傳遞,這也是因爲緩存Func部分的泛型難以直接傳遞轉換的方式,這個對使用並沒有太大影響。
爲了代碼通用,抽取了公共部分構造了一個內部的類進行對錶達式樹的構建操做:
1 /********************************************************* 2 * CopyRight: 7TINY CODE BUILDER. 3 * Version: 5.0.0 4 * Author: 7tiny 5 * Address: Earth 6 * Create: 2018-04-09 16:55:16 7 * Modify: 2018-04-09 16:55:16 8 * E-mail: dong@7tiny.com | sevenTiny@foxmail.com 9 * GitHub: https://github.com/sevenTiny 10 * Personal web site: http://www.7tiny.com 11 * Technical WebSit: http://www.cnblogs.com/7tiny/ 12 * Description: 13 * Thx , Best Regards ~ 14 *********************************************************/ 15 using System; 16 using System.Collections.Generic; 17 using System.Linq; 18 using System.Linq.Expressions; 19 using System.Reflection; 20 21 namespace SevenTiny.Bantina.AutoMapper 22 { 23 internal sealed class MapperExpressionCommon 24 { 25 /// <summary> 26 /// structure func 27 /// </summary> 28 /// <param name="outType"></param> 29 /// <param name="inTypes"></param> 30 /// <param name="memberInitExpression"></param> 31 /// <param name="parameterExpressionList"></param> 32 public static void GetFunc(Type outType, Type[] inTypes, out MemberInitExpression memberInitExpression, out List<ParameterExpression> parameterExpressionList) 33 { 34 parameterExpressionList = new List<ParameterExpression>(); 35 List<MemberBinding> memberBindingList = new List<MemberBinding>(); 36 PropertyInfo[] propertyInfos = outType.GetProperties(); 37 Dictionary<string, PropertyInfo> outPropertyDic = propertyInfos.ToDictionary(t => t.Name, t => t); 38 foreach (var inType in inTypes) 39 { 40 ParameterExpression parameterExpression = Expression.Parameter(inType, inType.FullName); 41 PropertyInfo[] inTypePpropertyInfos = inType.GetProperties(); 42 foreach (var inTypeInfo in inTypePpropertyInfos) 43 { 44 if (inTypeInfo.GetCustomAttribute(typeof(DoNotMapperAttribute)) == null) 45 { 46 //first 47 string outPropertyDicKey = MapperAttribute.GetTargetName(inTypeInfo); 48 //second 49 if (string.IsNullOrEmpty(outPropertyDicKey) && outPropertyDic.Keys.Contains(inTypeInfo.Name)) 50 { 51 outPropertyDicKey = inTypeInfo.Name; 52 } 53 //third 54 if (!string.IsNullOrEmpty(outPropertyDicKey) && outPropertyDic.Keys.Contains(outPropertyDicKey)) 55 { 56 MemberExpression property = Expression.Property(parameterExpression, inTypeInfo); 57 MemberBinding memberBinding = Expression.Bind(outPropertyDic[outPropertyDicKey], property); 58 memberBindingList.Add(memberBinding); 59 outPropertyDic.Remove(outPropertyDicKey);//remove property if has be valued 60 } 61 } 62 } 63 if (!parameterExpressionList.Exists(t => t.Name.Equals(parameterExpression.Name))) 64 { 65 parameterExpressionList.Add(parameterExpression); 66 } 67 } 68 memberInitExpression = Expression.MemberInit(Expression.New(outType), memberBindingList.ToArray()); 69 } 70 } 71 72 }
在上面的這段代碼中:
ParameterExpression parameterExpression = Expression.Parameter(inType, inType.FullName);
MemberExpression property = Expression.Property(parameterExpression, inTypeInfo);
MemberBinding memberBinding = Expression.Bind(outPropertyDic[outPropertyDicKey], property);
1 memberInitExpression = Expression.MemberInit(Expression.New(outType), memberBindingList.ToArray());
這段邏輯代碼的調用方代碼:
1 /********************************************************* 2 * CopyRight: 7TINY CODE BUILDER. 3 * Version: 5.0.0 4 * Author: 7tiny 5 * Address: Earth 6 * Create: 2018-03-16 10:11:43 7 * Modify: 2018-4-3 11:35:53 8 * E-mail: dong@7tiny.com | sevenTiny@foxmail.com 9 * GitHub: https://github.com/sevenTiny 10 * Personal web site: http://www.7tiny.com 11 * Technical WebSit: http://www.cnblogs.com/7tiny/ 12 * Description: 13 * Thx , Best Regards ~ 14 *********************************************************/ 15 using System; 16 using System.Collections.Generic; 17 using System.Linq; 18 using System.Linq.Expressions; 19 using System.Reflection; 20 21 namespace SevenTiny.Bantina.AutoMapper 22 { 23 public sealed class Mapper<TIn, TOut> where TOut : class where TIn : class 24 { 25 private Mapper() { } 26 private static readonly Func<TIn, TOut> funcCache = GetFunc(); 27 public static TOut AutoMapper(TIn tIn) 28 { 29 return funcCache(tIn); 30 } 31 public static TOut AutoMapper(TIn tIn, Action<TOut> action) 32 { 33 TOut outValue = funcCache(tIn); 34 action(outValue); 35 return outValue; 36 } 37 private static Func<TIn, TOut> GetFunc() 38 { 39 Type[] types = new Type[] { typeof(TIn) }; 40 MemberInitExpression memberInitExpression; 41 List<ParameterExpression> parameterExpressionList; 42 MapperExpressionCommon.GetFunc(typeof(TOut), types, out memberInitExpression, out parameterExpressionList); 43 Expression<Func<TIn, TOut>> lambda = Expression.Lambda<Func<TIn, TOut>>(memberInitExpression, parameterExpressionList); 44 return lambda.Compile(); 45 } 46 } 47 }
在上面這段代碼中:
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萬次
1 Student5 stu5 = new Student5 { HealthLevel = 100, SchoolClass = new SchoolClass { Name = "class1" } }; 2 3 var test1 = StopwatchHelper.Caculate(1000000, () => 4 { 5 Student stu = Mapper<Student5, Student>.AutoMapper(stu5, t => t.Name = "jony"); 6 }); 7 Console.WriteLine(test1.TotalMilliseconds);
咱們加大調用的次數再次進行比較:
1 Student5 stu5 = new Student5 { HealthLevel = 100, SchoolClass = new SchoolClass { Name = "class1" } }; 2 3 var test0 = StopwatchHelper.Caculate(1000000, () => 4 { 5 Student stu = Mapper.AutoMapper<Student,Student5>(stu5, t => t.Name = "jony"); 6 }); 7 Console.WriteLine("使用反射調用 1 百萬次耗時:"); 8 Console.WriteLine(test0.TotalMilliseconds); 9 10 Console.WriteLine(); 11 12 var test1 = StopwatchHelper.Caculate(1000000, () => 13 { 14 Student stu = Mapper<Student5, Student>.AutoMapper(stu5, t => t.Name = "jony"); 15 }); 16 Console.WriteLine("使用Expression表達式樹調用 1 百萬次耗時:"); 17 Console.WriteLine(test1.TotalMilliseconds); 18 19 Console.WriteLine(); 20 21 var test2 = StopwatchHelper.Caculate(1000000, () => 22 { 23 Student stu = new Student { HealthLevel = stu5.HealthLevel, Name = "jony" }; 24 }); 25 Console.WriteLine("使用代碼直接構建 1 百萬次耗時:"); 26 Console.WriteLine(test2.TotalMilliseconds);
經過本次基礎組建的完成以及一次優化,我從中學習到了Expression表達式樹的實現方法以及突出的性能優點,雖然相比反射的寫法更加複雜一些。
在接下來的優化中,可能會加入對Emit的支持,到時候即是一場Emit和Expression的大戰,不過我再次預測結果:應該差距不是很大~ 敬請期待...
本文的完整代碼以在個人github中開源,能夠直接clone下來查看驗證。
https://github.com/sevenTiny/SevenTiny.Bantina
下載下來的項目有多種基礎組件,直接查看該組件,固然有對其餘組件的意見或建議也能夠直接提出來探討,共同窗習哦~
不便於github查看的同窗,我這裏提供了完整的代碼(是有多個對象映射的重載方法的完整版本)。
公共Attribute特性標籤部分:
1 /********************************************************* 2 * CopyRight: 7TINY CODE BUILDER. 3 * Version: 5.0.0 4 * Author: 7tiny 5 * Address: Earth 6 * Create: 2018-04-09 16:55:16 7 * Modify: 2018-04-09 16:55:16 8 * E-mail: dong@7tiny.com | sevenTiny@foxmail.com 9 * GitHub: https://github.com/sevenTiny 10 * Personal web site: http://www.7tiny.com 11 * Technical WebSit: http://www.cnblogs.com/7tiny/ 12 * Description: 13 * Thx , Best Regards ~ 14 *********************************************************/ 15 using System; 16 17 namespace SevenTiny.Bantina.AutoMapper 18 { 19 [AttributeUsage(AttributeTargets.Property, Inherited = true)] 20 public class DoNotMapperAttribute :Attribute 21 { 22 } 23 }
1 /********************************************************* 2 * CopyRight: 7TINY CODE BUILDER. 3 * Version: 5.0.0 4 * Author: 7tiny 5 * Address: Earth 6 * Create: 2018-04-03 13:28:38 7 * Modify: 2018-04-03 13:28:38 8 * E-mail: dong@7tiny.com | sevenTiny@foxmail.com 9 * GitHub: https://github.com/sevenTiny 10 * Personal web site: http://www.7tiny.com 11 * Technical WebSit: http://www.cnblogs.com/7tiny/ 12 * Description: 13 * Thx , Best Regards ~ 14 *********************************************************/ 15 using System; 16 using System.Linq; 17 using System.Reflection; 18 19 namespace SevenTiny.Bantina.AutoMapper 20 { 21 [AttributeUsage(AttributeTargets.Property, Inherited = true)] 22 public class MapperAttribute : Attribute 23 { 24 public string TargetName { get; set; } 25 26 public MapperAttribute() { } 27 public MapperAttribute(string targetName) 28 { 29 this.TargetName = targetName; 30 } 31 32 public static string GetTargetName(PropertyInfo property) 33 { 34 var attr = property.GetCustomAttributes<MapperAttribute>(true).FirstOrDefault(); 35 return attr != null ? (attr as MapperAttribute).TargetName ?? default(string) : default(string); 36 } 37 } 38 }
1 /********************************************************* 2 * CopyRight: 7TINY CODE BUILDER. 3 * Version: 5.0.0 4 * Author: 7tiny 5 * Address: Earth 6 * Create: 2018-04-03 13:28:38 7 * Modify: 2018-04-03 13:28:38 8 * E-mail: dong@7tiny.com | sevenTiny@foxmail.com 9 * GitHub: https://github.com/sevenTiny 10 * Personal web site: http://www.7tiny.com 11 * Technical WebSit: http://www.cnblogs.com/7tiny/ 12 * Description: 13 * Thx , Best Regards ~ 14 *********************************************************/ 15 using System; 16 using System.Linq; 17 18 namespace SevenTiny.Bantina.AutoMapper 19 { 20 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Field, Inherited = true)] 21 public class MapperClassAttribute : Attribute 22 { 23 public string Name { get; set; } 24 public static string GetName(Type type) 25 { 26 var attr = type.GetCustomAttributes(typeof(MapperClassAttribute), true).FirstOrDefault(); 27 return attr != null ? (attr as MapperClassAttribute).Name ?? default(string) : default(string); 28 } 29 } 30 }
1、反射版本
1 /********************************************************* 2 * CopyRight: 7TINY CODE BUILDER. 3 * Version: 5.0.0 4 * Author: 7tiny 5 * Address: Earth 6 * Create: 2018-03-16 10:11:43 7 * Modify: 2018-4-3 11:35:53 8 * E-mail: dong@7tiny.com | sevenTiny@foxmail.com 9 * GitHub: https://github.com/sevenTiny 10 * Personal web site: http://www.7tiny.com 11 * Technical WebSit: http://www.cnblogs.com/7tiny/ 12 * Description: 13 * Thx , Best Regards ~ 14 *********************************************************/ 15 using System; 16 using System.Collections.Generic; 17 using System.Linq; 18 using System.Linq.Expressions; 19 using System.Reflection; 20 21 namespace SevenTiny.Bantina.AutoMapper 22 { 23 public sealed class Mapper 24 { 25 private Mapper() { } 26 /// <summary> 27 /// Init Source Value Dic 28 /// </summary> 29 /// <typeparam name="TSource"></typeparam> 30 /// <param name="source"></param> 31 /// <returns></returns> 32 private static Dictionary<string, object> Initdic<TSource>(TSource source) 33 { 34 Dictionary<string, object> dic = new Dictionary<string, object>(); 35 foreach (PropertyInfo property in typeof(TSource).GetProperties()) 36 { 37 string targetPropertyName = MapperAttribute.GetTargetName(property); 38 if (!string.IsNullOrEmpty(targetPropertyName)) 39 { 40 if (!dic.ContainsKey(targetPropertyName)) 41 { 42 dic.Add(targetPropertyName, property.GetValue(source)); 43 } 44 } 45 else if (!dic.ContainsKey(property.Name)) 46 { 47 dic.Add(property.Name, property.GetValue(source)); 48 } 49 } 50 return dic; 51 } 52 /// <summary> 53 /// SetValue from propertyinfo and sourceDictionary 54 /// </summary> 55 /// <typeparam name="TValue"></typeparam> 56 /// <param name="value"></param> 57 /// <param name="propertyInfos"></param> 58 /// <param name="sourceDic"></param> 59 /// <returns></returns> 60 private static TValue SetValue<TValue>(TValue value, PropertyInfo[] propertyInfos, Dictionary<string, object> sourceDic, Dictionary<string, string> keys) where TValue : class 61 { 62 foreach (PropertyInfo property in propertyInfos) 63 { 64 if (!keys.ContainsKey(property.Name)) 65 { 66 if (sourceDic.ContainsKey(property.Name)) 67 { 68 try 69 { 70 property.SetValue(value, sourceDic[property.Name]); 71 keys.Add(property.Name, string.Empty); 72 } 73 catch (Exception) 74 { 75 property.SetValue(value, null); 76 } 77 } 78 } 79 } 80 return value; 81 } 82 /// <summary> 83 /// AutoMapper 84 /// </summary> 85 /// <typeparam name="TValue">value type</typeparam> 86 /// <typeparam name="TSource">source type</typeparam> 87 /// <param name="source"></param> 88 /// <returns></returns> 89 public static TValue AutoMapper<TValue, TSource>(TSource source) where TValue : class where TSource : class 90 { 91 TValue value = Activator.CreateInstance<TValue>(); 92 PropertyInfo[] propertyInfos = typeof(TValue).GetProperties(); 93 Dictionary<string, string> keys = new Dictionary<string, string>(); 94 value = SetValue(value, propertyInfos, Initdic(source), keys); 95 return value; 96 } 97 /// <summary> 98 /// AutoMapper,Support for Use Action to custom special fields. 99 /// </summary> 100 /// <typeparam name="TValue"></typeparam> 101 /// <typeparam name="TSource"></typeparam> 102 /// <param name="source"></param> 103 /// <param name="action"></param> 104 /// <returns></returns> 105 public static TValue AutoMapper<TValue, TSource>(TSource source, Action<TValue> action) where TValue : class where TSource : class 106 { 107 TValue value = Activator.CreateInstance<TValue>(); 108 PropertyInfo[] propertyInfos = typeof(TValue).GetProperties(); 109 Dictionary<string, string> keys = new Dictionary<string, string>(); 110 value = SetValue(value, propertyInfos, Initdic(source), keys); 111 action(value); 112 return value; 113 } 114 /// <summary> 115 /// AutoMapper with multitype properties. 116 /// </summary> 117 /// <typeparam name="TValue"></typeparam> 118 /// <typeparam name="TSource1"></typeparam> 119 /// <typeparam name="TSource2"></typeparam> 120 /// <param name="source1"></param> 121 /// <param name="source2"></param> 122 /// <returns></returns> 123 public static TValue AutoMapper<TValue, TSource1, TSource2>(TSource1 source1, TSource2 source2) where TValue : class where TSource1 : class where TSource2 : class 124 { 125 TValue value = Activator.CreateInstance<TValue>(); 126 PropertyInfo[] propertyInfos = typeof(TValue).GetProperties(); 127 Dictionary<string, string> keys = new Dictionary<string, string>(); 128 value = SetValue(value, propertyInfos, Initdic(source1), keys); 129 value = SetValue(value, propertyInfos, Initdic(source2), keys); 130 return value; 131 } 132 /// <summary> 133 /// AutoMapper with multitype properties.Support for Use Action to custom special fields. 134 /// </summary> 135 /// <typeparam name="TValue"></typeparam> 136 /// <typeparam name="TSource1"></typeparam> 137 /// <typeparam name="TSource2"></typeparam> 138 /// <param name="source1"></param> 139 /// <param name="source2"></param> 140 /// <param name="action"></param> 141 /// <returns></returns> 142 public static TValue AutoMapper<TValue, TSource1, TSource2>(TSource1 source1, TSource2 source2, Action<TValue> action) where TValue : class where TSource1 : class where TSource2 : class 143 { 144 TValue value = Activator.CreateInstance<TValue>(); 145 PropertyInfo[] propertyInfos = typeof(TValue).GetProperties(); 146 Dictionary<string, string> keys = new Dictionary<string, string>(); 147 value = SetValue(value, propertyInfos, Initdic(source1), keys); 148 value = SetValue(value, propertyInfos, Initdic(source2), keys); 149 action(value); 150 return value; 151 } 152 /// <summary> 153 /// AutoMapper with multitype properties. 154 /// </summary> 155 /// <typeparam name="TValue"></typeparam> 156 /// <typeparam name="TSource1"></typeparam> 157 /// <typeparam name="TSource2"></typeparam> 158 /// <typeparam name="TSource3"></typeparam> 159 /// <param name="source1"></param> 160 /// <param name="source2"></param> 161 /// <param name="source3"></param> 162 /// <returns></returns> 163 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 164 { 165 TValue value = Activator.CreateInstance<TValue>(); 166 PropertyInfo[] propertyInfos = typeof(TValue).GetProperties(); 167 Dictionary<string, string> keys = new Dictionary<string, string>(); 168 value = SetValue(value, propertyInfos, Initdic(source1), keys); 169 value = SetValue(value, propertyInfos, Initdic(source2), keys); 170 value = SetValue(value, propertyInfos, Initdic(source3), keys); 171 return value; 172 } 173 /// <summary> 174 /// AutoMapper with multitype properties.Support for Use Action to custom special fields. 175 /// </summary> 176 /// <typeparam name="TValue"></typeparam> 177 /// <typeparam name="TSource1"></typeparam> 178 /// <typeparam name="TSource2"></typeparam> 179 /// <typeparam name="TSource3"></typeparam> 180 /// <param name="source1"></param> 181 /// <param name="source2"></param> 182 /// <param name="source3"></param> 183 /// <param name="action"></param> 184 /// <returns></returns> 185 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 186 { 187 TValue value = Activator.CreateInstance<TValue>(); 188 PropertyInfo[] propertyInfos = typeof(TValue).GetProperties(); 189 Dictionary<string, string> keys = new Dictionary<string, string>(); 190 value = SetValue(value, propertyInfos, Initdic(source1), keys); 191 value = SetValue(value, propertyInfos, Initdic(source2), keys); 192 value = SetValue(value, propertyInfos, Initdic(source3), keys); 193 action(value); 194 return value; 195 } 196 /// <summary> 197 /// AutoMapper with multitype properties. 198 /// </summary> 199 /// <typeparam name="TValue"></typeparam> 200 /// <typeparam name="TSource1"></typeparam> 201 /// <typeparam name="TSource2"></typeparam> 202 /// <typeparam name="TSource3"></typeparam> 203 /// <typeparam name="TSource4"></typeparam> 204 /// <param name="source1"></param> 205 /// <param name="source2"></param> 206 /// <param name="source3"></param> 207 /// <param name="source4"></param> 208 /// <returns></returns> 209 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 210 { 211 TValue value = Activator.CreateInstance<TValue>(); 212 PropertyInfo[] propertyInfos = typeof(TValue).GetProperties(); 213 Dictionary<string, string> keys = new Dictionary<string, string>(); 214 value = SetValue(value, propertyInfos, Initdic(source1), keys); 215 value = SetValue(value, propertyInfos, Initdic(source2), keys); 216 value = SetValue(value, propertyInfos, Initdic(source3), keys); 217 value = SetValue(value, propertyInfos, Initdic(source4), keys); 218 return value; 219 } 220 /// <summary> 221 /// AutoMapper with multitype properties.Support for Use Action to custom special fields. 222 /// </summary> 223 /// <typeparam name="TValue"></typeparam> 224 /// <typeparam name="TSource1"></typeparam> 225 /// <typeparam name="TSource2"></typeparam> 226 /// <typeparam name="TSource3"></typeparam> 227 /// <typeparam name="TSource4"></typeparam> 228 /// <param name="source1"></param> 229 /// <param name="source2"></param> 230 /// <param name="source3"></param> 231 /// <param name="source4"></param> 232 /// <param name="action"></param> 233 /// <returns></returns> 234 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 235 { 236 TValue value = Activator.CreateInstance<TValue>(); 237 PropertyInfo[] propertyInfos = typeof(TValue).GetProperties(); 238 Dictionary<string, string> keys = new Dictionary<string, string>(); 239 value = SetValue(value, propertyInfos, Initdic(source1), keys); 240 value = SetValue(value, propertyInfos, Initdic(source2), keys); 241 value = SetValue(value, propertyInfos, Initdic(source3), keys); 242 value = SetValue(value, propertyInfos, Initdic(source4), keys); 243 action(value); 244 return value; 245 } 246 /// <summary> 247 /// AutoMapper with multitype properties. 248 /// </summary> 249 /// <typeparam name="TValue"></typeparam> 250 /// <typeparam name="TSource1"></typeparam> 251 /// <typeparam name="TSource2"></typeparam> 252 /// <typeparam name="TSource3"></typeparam> 253 /// <typeparam name="TSource4"></typeparam> 254 /// <typeparam name="TSource5"></typeparam> 255 /// <param name="source1"></param> 256 /// <param name="source2"></param> 257 /// <param name="source3"></param> 258 /// <param name="source4"></param> 259 /// <param name="source5"></param> 260 /// <returns></returns> 261 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 262 { 263 TValue value = Activator.CreateInstance<TValue>(); 264 PropertyInfo[] propertyInfos = typeof(TValue).GetProperties(); 265 Dictionary<string, string> keys = new Dictionary<string, string>(); 266 value = SetValue(value, propertyInfos, Initdic(source1), keys); 267 value = SetValue(value, propertyInfos, Initdic(source2), keys); 268 value = SetValue(value, propertyInfos, Initdic(source3), keys); 269 value = SetValue(value, propertyInfos, Initdic(source4), keys); 270 value = SetValue(value, propertyInfos, Initdic(source5), keys); 271 return value; 272 } 273 /// <summary> 274 /// AutoMapper with multitype properties.Support for Use Action to custom special fields. 275 /// </summary> 276 /// <typeparam name="TValue"></typeparam> 277 /// <typeparam name="TSource1"></typeparam> 278 /// <typeparam name="TSource2"></typeparam> 279 /// <typeparam name="TSource3"></typeparam> 280 /// <typeparam name="TSource4"></typeparam> 281 /// <typeparam name="TSource5"></typeparam> 282 /// <param name="source1"></param> 283 /// <param name="source2"></param> 284 /// <param name="source3"></param> 285 /// <param name="source4"></param> 286 /// <param name="source5"></param> 287 /// <param name="action"></param> 288 /// <returns></returns> 289 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 290 { 291 TValue value = Activator.CreateInstance<TValue>(); 292 PropertyInfo[] propertyInfos = typeof(TValue).GetProperties(); 293 Dictionary<string, string> keys = new Dictionary<string, string>(); 294 value = SetValue(value, propertyInfos, Initdic(source1), keys); 295 value = SetValue(value, propertyInfos, Initdic(source2), keys); 296 value = SetValue(value, propertyInfos, Initdic(source3), keys); 297 value = SetValue(value, propertyInfos, Initdic(source4), keys); 298 value = SetValue(value, propertyInfos, Initdic(source5), keys); 299 action(value); 300 return value; 301 } 302 } 303 }
2、Expression Tree 表達式樹版本
1 /********************************************************* 2 * CopyRight: 7TINY CODE BUILDER. 3 * Version: 5.0.0 4 * Author: 7tiny 5 * Address: Earth 6 * Create: 2018-03-16 10:11:43 7 * Modify: 2018-4-3 11:35:53 8 * E-mail: dong@7tiny.com | sevenTiny@foxmail.com 9 * GitHub: https://github.com/sevenTiny 10 * Personal web site: http://www.7tiny.com 11 * Technical WebSit: http://www.cnblogs.com/7tiny/ 12 * Description: 13 * Thx , Best Regards ~ 14 *********************************************************/ 15 using System; 16 using System.Collections.Generic; 17 using System.Linq; 18 using System.Linq.Expressions; 19 using System.Reflection; 20 21 namespace SevenTiny.Bantina.AutoMapper 22 { 23 public sealed class Mapper<TIn, TOut> where TOut : class where TIn : class 24 { 25 private Mapper() { } 26 private static readonly Func<TIn, TOut> funcCache = GetFunc(); 27 public static TOut AutoMapper(TIn tIn) 28 { 29 return funcCache(tIn); 30 } 31 public static TOut AutoMapper(TIn tIn, Action<TOut> action) 32 { 33 TOut outValue = funcCache(tIn); 34 action(outValue); 35 return outValue; 36 } 37 private static Func<TIn, TOut> GetFunc() 38 { 39 Type[] types = new Type[] { typeof(TIn) }; 40 MemberInitExpression memberInitExpression; 41 List<ParameterExpression> parameterExpressionList; 42 MapperExpressionCommon.GetFunc(typeof(TOut), types, out memberInitExpression, out parameterExpressionList); 43 Expression<Func<TIn, TOut>> lambda = Expression.Lambda<Func<TIn, TOut>>(memberInitExpression, parameterExpressionList); 44 return lambda.Compile(); 45 } 46 } 47 public sealed class Mapper<TIn1, TIn2, TOut> where TOut : class where TIn1 : class where TIn2 : class 48 { 49 private Mapper() { } 50 public static TOut AutoMapper(TIn1 tIn1, TIn2 tIn2) 51 { 52 return funcCache(tIn1, tIn2); 53 } 54 public static TOut AutoMapper(TIn1 tIn1, TIn2 tIn2, Action<TOut> action) 55 { 56 TOut outValue = funcCache(tIn1, tIn2); 57 action(outValue); 58 return outValue; 59 } 60 private static readonly Func<TIn1, TIn2, TOut> funcCache = GetFunc(); 61 private static Func<TIn1, TIn2, TOut> GetFunc() 62 { 63 Type[] types = new Type[] { typeof(TIn1), typeof(TIn2) }; 64 MemberInitExpression memberInitExpression; 65 List<ParameterExpression> parameterExpressionList; 66 MapperExpressionCommon.GetFunc(typeof(TOut), types, out memberInitExpression, out parameterExpressionList); 67 Expression<Func<TIn1, TIn2, TOut>> lambda = Expression.Lambda<Func<TIn1, TIn2, TOut>>(memberInitExpression, parameterExpressionList); 68 return lambda.Compile(); 69 } 70 } 71 public sealed class Mapper<TIn1, TIn2, TIn3, TOut> where TOut : class where TIn1 : class where TIn2 : class where TIn3 : class 72 { 73 private Mapper() { } 74 public static TOut AutoMapper(TIn1 tIn1, TIn2 tIn2, TIn3 tIn3) 75 { 76 return funcCache(tIn1, tIn2, tIn3); 77 } 78 public static TOut AutoMapper(TIn1 tIn1, TIn2 tIn2, TIn3 tIn3, Action<TOut> action) 79 { 80 TOut outValue = funcCache(tIn1, tIn2, tIn3); 81 action(outValue); 82 return outValue; 83 } 84 private static readonly Func<TIn1, TIn2, TIn3, TOut> funcCache = GetFunc(); 85 private static Func<TIn1, TIn2, TIn3, TOut> GetFunc() 86 { 87 Type[] types = new Type[] { typeof(TIn1), typeof(TIn2), typeof(TIn3) }; 88 MemberInitExpression memberInitExpression; 89 List<ParameterExpression> parameterExpressionList; 90 MapperExpressionCommon.GetFunc(typeof(TOut), types, out memberInitExpression, out parameterExpressionList); 91 Expression<Func<TIn1, TIn2, TIn3, TOut>> lambda = Expression.Lambda<Func<TIn1, TIn2, TIn3, TOut>>(memberInitExpression, parameterExpressionList); 92 return lambda.Compile(); 93 } 94 } 95 public sealed class Mapper<TIn1, TIn2, TIn3, TIn4, TOut> where TOut : class where TIn1 : class where TIn2 : class where TIn3 : class where TIn4 : class 96 { 97 private Mapper() { } 98 public static TOut AutoMapper(TIn1 tIn1, TIn2 tIn2, TIn3 tIn3, TIn4 tIn4) 99 { 100 return funcCache(tIn1, tIn2, tIn3, tIn4); 101 } 102 public static TOut AutoMapper(TIn1 tIn1, TIn2 tIn2, TIn3 tIn3, TIn4 tIn4, Action<TOut> action) 103 { 104 TOut outValue = funcCache(tIn1, tIn2, tIn3, tIn4); 105 action(outValue); 106 return outValue; 107 } 108 private static readonly Func<TIn1, TIn2, TIn3, TIn4, TOut> funcCache = GetFunc(); 109 private static Func<TIn1, TIn2, TIn3, TIn4, TOut> GetFunc() 110 { 111 Type[] types = new Type[] { typeof(TIn1), typeof(TIn2), typeof(TIn3), typeof(TIn4) }; 112 MemberInitExpression memberInitExpression; 113 List<ParameterExpression> parameterExpressionList; 114 MapperExpressionCommon.GetFunc(typeof(TOut), types, out memberInitExpression, out parameterExpressionList); 115 Expression<Func<TIn1, TIn2, TIn3, TIn4, TOut>> lambda = Expression.Lambda<Func<TIn1, TIn2, TIn3, TIn4, TOut>>(memberInitExpression, parameterExpressionList); 116 return lambda.Compile(); 117 } 118 } 119 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 120 { 121 private Mapper() { } 122 public static TOut AutoMapper(TIn1 tIn1, TIn2 tIn2, TIn3 tIn3, TIn4 tIn4, TIn5 tIn5) 123 { 124 return funcCache(tIn1, tIn2, tIn3, tIn4, tIn5); 125 } 126 public static TOut AutoMapper(TIn1 tIn1, TIn2 tIn2, TIn3 tIn3, TIn4 tIn4, TIn5 tIn5, Action<TOut> action) 127 { 128 TOut outValue = funcCache(tIn1, tIn2, tIn3, tIn4, tIn5); 129 action(outValue); 130 return outValue; 131 } 132 private static readonly Func<TIn1, TIn2, TIn3, TIn4, TIn5, TOut> funcCache = GetFunc(); 133 private static Func<TIn1, TIn2, TIn3, TIn4, TIn5, TOut> GetFunc() 134 { 135 Type[] types = new Type[] { typeof(TIn1), typeof(TIn2), typeof(TIn3), typeof(TIn4), typeof(TIn5) }; 136 MemberInitExpression memberInitExpression; 137 List<ParameterExpression> parameterExpressionList; 138 MapperExpressionCommon.GetFunc(typeof(TOut), types, out memberInitExpression, out parameterExpressionList); 139 Expression<Func<TIn1, TIn2, TIn3, TIn4, TIn5, TOut>> lambda = Expression.Lambda<Func<TIn1, TIn2, TIn3, TIn4, TIn5, TOut>>(memberInitExpression, parameterExpressionList); 140 return lambda.Compile(); 141 } 142 } 143 }