.NetCore 利用反射處理RESTful的更新

背景

在作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);
相關文章
相關標籤/搜索