在RESTful Api模式開發Api,Patch更新時,頻繁的遇到不一樣類型但屬性相同的Dto到Model字段賦值的狀況,
網上通用的類庫是AutoMapper,但遇到了問題,查了Git也提出了問題,並未能解決。app
Post、Get等覆蓋式傳值時都能知足需求,惟獨當Dto字段的值類型設爲Nullable(如int?、bool?)時會被覆蓋爲默認值。spa
一、Lambda表達式
二、反射code
利用反射查找類的字段名稱與字段類型,並將具有相同屬性名的源Class屬性值賦值到目標Class,
如遇到源Class屬爲Null(引用空 或 Nullable泛型類型Null)跳過該屬賦值。orm
using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Linq.Expressions; using System.Reflection; namespace Zhiqing.Helpers.Lambdas { /// <summary> /// 快速複製,並忽略NULL值 /// </summary> public static class FastCopy { static Action<S, T> CreateCopier<S, T>() { // 源 var source = Expression.Parameter(typeof(S)); // 目標 var target = Expression.Parameter(typeof(T)); // 源 全部屬性 var props1 = typeof(S).GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.CanRead).ToList(); // 目標 全部屬性 var props2 = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.CanWrite).ToList(); // 共有屬性 var props = props1.Where(x => props2.Any(y => y.Name == x.Name)); IEnumerable<Expression> assets = new List<Expression>(); foreach (var item in props) { // 目標 var tItem = Expression.Property(target, item.Name); var tType = tItem.Type; var tIsNullable = tType.IsGenericType && tType.GetGenericTypeDefinition() == typeof(Nullable<>); // 源 var sItem = Expression.Property(source, item.Name); var sType = sItem.Type; var sIsNullable = sType.IsGenericType && sType.GetGenericTypeDefinition() == typeof(Nullable<>); Debug.WriteLine(sIsNullable); // =================================== // 註釋:Nullable實際是個泛型,賦值是須要轉爲實際類型纔可賦值,否咋泛型給實際類型賦值引起異常 // 案例:int? s = 1;int t = s; 會引起異常 // 解決:int? s = 1;int t = Convert.ToInt32(s); 轉換後解決 // 另外:Lamnda表達式應使用 Expression.Convert(); 轉換 // 源是可爲空類型 if (sIsNullable) { // 目標可爲空 if (tIsNullable) { // 賦值表達式 var asset = Expression.Assign(tItem, sItem); // 當源不爲空的時候賦值 var notNull = Expression.IfThen(Expression.NotEqual(sItem, Expression.Constant(null)), asset); // 加入表達式樹 assets = assets.Append(notNull); } // 目標不可爲空 else { // 轉換源爲實際類型 var sItemConverted = Expression.Convert(sItem, sType.GetGenericArguments().First()); // 賦值表達式 var asset = Expression.Assign(tItem, sItemConverted); // 當源不爲空的時候賦值 var notNull = Expression.IfThen(Expression.NotEqual(sItem, Expression.Constant(null)), asset); // 加入表達式樹 assets = assets.Append(notNull); } } // 源不是可爲空類型 else { // 源是否值類型 var sIsValueType = sType.IsValueType; if (sIsValueType) { // 賦值表達式 var asset = Expression.Assign(tItem, sItem); // 加入表達式樹 assets = assets.Append(asset); } // 不是值類型 else { // 賦值表達式 var asset = Expression.Assign(tItem, sItem); // 當源不爲空的時候賦值 var notNull = Expression.IfThen(Expression.NotEqual(sItem, Expression.Constant(null)), asset); // 加入表達式樹 assets = assets.Append(notNull); } } } // 賦值 var tempBlock = Expression.Block(assets); return Expression.Lambda<Action<S, T>>(tempBlock, source, target).Compile(); } static ConcurrentDictionary<string, object> actions = new ConcurrentDictionary<string, object>(); /// <summary> /// 快速的拷貝同名公共屬性。忽略差別的字段。 /// </summary> /// <typeparam name="S"></typeparam> /// <typeparam name="T"></typeparam> /// <param name="from"></param> /// <param name="to"></param> public static void Copy<S, T>(S from, T to) { string name = string.Format("{0}_{1}", typeof(S), typeof(T)); if (!actions.TryGetValue(name, out object obj)) { var ff = CreateCopier<S, T>(); actions.TryAdd(name, ff); obj = ff; } var act = (Action<S, T>)obj; act(from, to); } } }
FastCopy.Copy<Card_UpdateDto, Commons.Entities.Card>(updateDto, modelCard);