在作RESTful api模式開發Api時,Patch更新時,頻繁的遇到不一樣類型但屬性相同的Dto到Model賦值的狀況,api
網上通用的類庫是AutoMapper,但遇到了問題,查了Git也提出了問題,並未能解決。app
Post、Get等覆蓋式傳值時都能知足需求,惟獨當Dto的值類型字段設爲Nullable(如int?、bool?)時會被覆蓋爲默認值。spa
Lambda表達式、反射code
利用反射查找類的字段名稱與字段類型,並將具有相同屬性名的源class屬性值賦值到目標class,如遇到源class屬爲Null(引用空 或 Nullable泛型類型Null)跳過該屬賦值。orm
1 using System; 2 using System.Collections.Concurrent; 3 using System.Collections.Generic; 4 using System.Diagnostics; 5 using System.Linq; 6 using System.Linq.Expressions; 7 using System.Reflection; 8 9 namespace Zhiqing.Helpers.Lambdas 10 { 11 /// <summary> 12 /// 快速複製,並忽略NULL值 13 /// </summary> 14 public static class FastCopy 15 { 16 static Action<S, T> CreateCopier<S, T>() 17 { 18 // 源 19 var source = Expression.Parameter(typeof(S)); 20 // 目標 21 var target = Expression.Parameter(typeof(T)); 22 // 源 全部屬性 23 var props1 = typeof(S).GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.CanRead).ToList(); 24 // 目標 全部屬性 25 var props2 = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.CanWrite).ToList(); 26 // 共有屬性 27 var props = props1.Where(x => props2.Any(y => y.Name == x.Name)); 28 29 30 IEnumerable<Expression> assets = new List<Expression>(); 31 32 foreach (var item in props) 33 { 34 // 目標 35 var tItem = Expression.Property(target, item.Name); 36 var tType = tItem.Type; 37 var tIsNullable = tType.IsGenericType && tType.GetGenericTypeDefinition() == typeof(Nullable<>); 38 39 // 源 40 var sItem = Expression.Property(source, item.Name); 41 var sType = sItem.Type; 42 var sIsNullable = sType.IsGenericType && sType.GetGenericTypeDefinition() == typeof(Nullable<>); 43 Debug.WriteLine(sIsNullable); 44 45 // =================================== 46 // 註釋:Nullable實際是個泛型,賦值是須要轉爲實際類型纔可賦值,否咋泛型給實際類型賦值引起異常 47 // 案例:int? s = 1;int t = s; 會引起異常 48 // 解決:int? s = 1;int t = Convert.ToInt32(s); 轉換後解決 49 // 另外:Lamnda表達式應使用 Expression.Convert(); 轉換 50 // 源是可爲空類型 51 if (sIsNullable) 52 { 53 // 目標可爲空 54 if (tIsNullable) 55 { 56 // 賦值表達式 57 var asset = Expression.Assign(tItem, sItem); 58 // 當源不爲空的時候賦值 59 var notNull = Expression.IfThen(Expression.NotEqual(sItem, Expression.Constant(null)), asset); 60 // 加入表達式樹 61 assets = assets.Append(notNull); 62 } 63 // 目標不可爲空 64 else 65 { 66 // 轉換源爲實際類型 67 var sItemConverted = Expression.Convert(sItem, sType.GetGenericArguments().First()); 68 // 賦值表達式 69 var asset = Expression.Assign(tItem, sItemConverted); 70 // 當源不爲空的時候賦值 71 var notNull = Expression.IfThen(Expression.NotEqual(sItem, Expression.Constant(null)), asset); 72 // 加入表達式樹 73 assets = assets.Append(notNull); 74 } 75 } 76 // 源不是可爲空類型 77 else 78 { 79 // 源是否值類型 80 var sIsValueType = sType.IsValueType; 81 if (sIsValueType) 82 { 83 // 賦值表達式 84 var asset = Expression.Assign(tItem, sItem); 85 // 加入表達式樹 86 assets = assets.Append(asset); 87 } 88 // 不是值類型 89 else 90 { 91 // 賦值表達式 92 var asset = Expression.Assign(tItem, sItem); 93 // 當源不爲空的時候賦值 94 var notNull = Expression.IfThen(Expression.NotEqual(sItem, Expression.Constant(null)), asset); 95 // 加入表達式樹 96 assets = assets.Append(notNull); 97 } 98 } 99 } 100 101 // 賦值 102 var tempBlock = Expression.Block(assets); 103 return Expression.Lambda<Action<S, T>>(tempBlock, source, target).Compile(); 104 } 105 106 static ConcurrentDictionary<string, object> actions = new ConcurrentDictionary<string, object>(); 107 108 /// <summary> 109 /// 快速的拷貝同名公共屬性。忽略差別的字段。 110 /// </summary> 111 /// <typeparam name="S"></typeparam> 112 /// <typeparam name="T"></typeparam> 113 /// <param name="from"></param> 114 /// <param name="to"></param> 115 public static void Copy<S, T>(S from, T to) 116 { 117 string name = string.Format("{0}_{1}", typeof(S), typeof(T)); 118 119 if (!actions.TryGetValue(name, out object obj)) 120 { 121 var ff = CreateCopier<S, T>(); 122 actions.TryAdd(name, ff); 123 obj = ff; 124 } 125 var act = (Action<S, T>)obj; 126 act(from, to); 127 } 128 129 130 } 131 }
1 FastCopy.Copy<Card_UpdateDto, Commons.Entities.Card>(updateDto, modelCard);